The two programs below get n integers from file and calculates the sum of ath to bth integers q(number of question) times. I think the upper program has worse time complexity than the lower, but I'm having problems calculating the time complexity of these two algorithms.
[input sample]
5 3
5 4 3 2 1
2 3
3 4
2 4
[output sample]
7
5
9
Program 1:
#include <stdio.h>
FILE *in=fopen("input.txt","r");
FILE *out=fopen("output.txt","w");
int n,q,a,b,sum;
int data[1000];
int main()
int i,j;
fscanf(in,"%d%d",&n,&q);
for(i=1;i<=n;i++) fscanf(in,"%d",&data[i]);
for i=0;i<q;i++)
{
fscanf(in,"%d%d",&a,&b);
sum=0;
for(j=a;j<=b;j++) sum+=data[j];
fprintf(out,"%d\n",sum);
}
return 0;
}
Program 2:
#include <stdio.h>
FILE *in=fopen("input.txt","r");
FILE *out=fopen("output.txt","w");
int n,q,a,b;
int data[1000];
int sum[1000];
int main()
{
int i,j;
fscanf(in,"%d%d",&n,&q);
for(i=1;i<=n;i++) fscanf(in,"%d",&data[i]);
for(i=1;i<=n;i++) sum[i]=sum[i-1]+data[i];
for(i=0;i<q;i++)
{
fscanf(in,"%d%d",&a,&b);
fprintf(out,"%d\n",sum[b]-sum[a-1]);
}
return 0;
}
The programs below gets n integers from 1 to m and sorts them. Again, I cannot calculate the time complexity.
[input sample]
5 5
2 1 3 4 5
[output sample]
1 2 3 4 5
Program:
#include <stdio.h>
FILE *in=fopen("input.txt","r")
FILE *out=fopen("output.txt","w")
int n,m;
int data[1000];
int count[1000];
int main()
{
int i,j;
fscanf(in,"%d%d",&n,&m);
for(i=0;i<n;i++)
{
fscanf(in,"%d",&data[i]);
count[data[i]]++
}
for(i=1;i<=m;i++)
{
for(j=0;j<count[i];j++) fprintf(out,"%d ",i);
}
return 0;
}
It's ironic(or not) that I cannot calculate the time complexity of my own algorithms, but I have passions to learn, so please programming gurus, help me!
What you need to do is pay attention to the loops, specifically how many times the loops repeat, and how much time is spent inside the loops. You need to multiple the number of times the outer loop repeats by the amount of time it takes inside the loop... if there is a inner loop, you multiply the number of repititions of the outer loop by the number of repititions of the inner loop, for example, to get the time complexity.
In your first program, you have one loop that takes O(n) time followed by a loop that takes O(q*(b-a)) time... it isn't exactly clear to me what b and a represent... but if you can bound b-a (let's say, you know that b-a < n), then you can express this more simply (e.g. if b-a < n, then you would say it was O(q*n)). The overall runtime would be the sum of those two terms, or, if one term is always bigger than the other, use the bigger term.
In the second program, you have two loops, each taking O(n) time, followed by a loop that takes O(q) time. So, the overall runtime is O(n+q). Note that if one term dominates the other, you can drop the term that is smaller. Even without knowing the value of (b-a), it is already apparent that this is better than the first one.
In the third program, the overall runtime is O(n+m), because you have one loop that takes O(n) time followed by a loop that takes O(m) time. If you know that m < n or vice-versa, you can simplify the expression by dropping the dominating term. If they can vary so that you don't know that one dominates the other, then writing it out as O(m+n) is the best you can do in terms of stating the time-complexity.
I should also point out that even if a loop is performed more than once, but it is performed a fixed number of times (e.g. in program 2, you have two loops that take O(n) time), it doesn't affect the time-complexity, because O(2n) is the same as O(n); in other words, constant factors don't matter in big-Oh complexity analysis. Also, if you have an inner loop that varies in terms of the number of times it is executed, if you are performing "worst-case" complexity analysis, you only need to know the worst possible value it can have.
For example, consider the following loop:
for (int i = 0; i < n; i++ ){
for (int j = i+1; j < n; j++ ){
// something taking O(1) time
}
}
The loop above takes O(n^2) time, even though not all the inner loops will take O(n) time.
I would also like to add that you should do a better job of formatting your program. Even though braces are not strictly required when an if/for/while statement only has one statement in the body, it is much more readable to use the braces anyway, and to use a newline. For example, it is much more readable if you write:
for (int i=1; i<=n; i++) {
sum[i]=sum[i-1]+data[i];
}
Than writing it as for (i=1; i<=n; i++) sum[i]=sum[i-1]+data[i];. Also, I should point out that even though you have tagged this question as C++, you are using C-like code... in C++, you can declare variables in the initialization of the for-loop (I suggest you do so). Also, in C++, the iostreams library (std::cin, std::cout, std::fstream, std::ostringstream, std::istringstream, etc.) are preferred over C FILE* objects.
You may also be interested in the following resource:
Stanford University: Analysis of Algorithms from Google Code University
Related
I am using this program written in C to determine the permutations of size 10 of an regular alphabet.
When I run the program it only uses 36% of my 3GHz CPU leaving 50% free. It also only uses 7MB of my 8GB of RAM.
I would like to use at least 70-80% of my computer's performance and not just this misery. This limitation is making this procedure very time consuming and I don't know when (number of days) I will need to have the complete output. I need help to resolve this issue in the shortest possible time, whether improving the source code or other possibilities.
Any help is welcome even if this solution goes through instead of using the C language use another one that gives me better performance in the execution of the program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int count = 0;
void print_permutations(char arr[], char prefix[], int n, int k) {
int i, j, l = strlen(prefix);
char newprefix[l + 2];
if (k == 0) {
printf("%d %s\n", ++count, prefix);
return;
}
for (i = 0; i < n; i++) {
//Concatenation of currentPrefix + arr[i] = newPrefix
for (j = 0; j < l; j++)
newprefix[j] = prefix[j];
newprefix[l] = arr[i];
newprefix[l + 1] = '\0';
print_permutations(arr, newprefix, n, k - 1);
}
}
int main() {
int n = 26, k = 10;
char arr[27] = "abcdefghijklmnopqrstuvwxyz";
print_permutations(arr, "", n, k);
system("pause");
return 0;
}
There are fundamental problems with your approach:
What are you trying to achieve?
If you want to enumerate the permutations of size 10 of a regular alphabet, your program is flawed as it enumerates all combinations of 10 letters from the alphabet. Your program will produce 2610 combinations, a huge number, 141167095653376, 141,167 billion! Ignoring the numbering, which will exceed the range of type int, that's more than 1.5 Petabytes, unlikely to fit on your storage space. Writing this at the top speed of 100MB/s would take more than 20 days.
The number of permutations, that is combinations of distinct letters from the 26 letter alphabet is not quite as large: 26! / 16! which is still large: 19275223968000, 7 times less than the previous result. That is still more than 212 terabytes of storage and 3 days at 100MB/s.
Storing these permutations is therefore impractical. You could change your program to just count the permutations and measure how long it takes if the count is the expected value. The first step of course is to correct your program to produce the correct set.
Test on smaller sets to verify correctness
Given the expected size of the problem, you should first test for smaller values such as enumerating permutations of 1, 2 and 3 letters to verify that you get the expected number of results.
Once you have correctness, only then focus on performance
Selecting different output methods, from printf("%d %s\n", ++count, prefix); to ++count; puts(prefix); to just ++count;, you will see that most of the time is spent in producing the output. Once you stop producing output, you might see that strlen() consumes a significant fraction of the execution time, which is useless since you can pass the prefix length from the caller. Further improvements may come from using a common array for the current prefix, precluding the need to copy at each recursive step.
Using multiple threads each producing its own output, for example each with a different initial letter, will not improve the overall time as the bottleneck is the bandwidth of the output device. But if you reduce the program to just enumerate and count the permutations, you might get faster execution with multiple threads, one per core, thereby increasing the CPU usage. But this should be the last step in your development.
Memory use is no measure of performance
Using as much memory as possible is not a goal in itself. Some problems may require a tradeoff between memory and time, where faster solving times are achieved using more core memory, but this one does not. 8MB is actually much more than your program's actual needs: this count includes the full stack space assigned to the program, of which only a tiny fraction will be used.
As a matter of fact, using less memory may improve overall performance as the CPU will make better use of its different caches.
Here is a modified program:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static unsigned long long count;
void print_permutations(char arr[], int n, char used[], char prefix[], int pos, int k) {
if (pos == k) {
prefix[k] = '\0';
++count;
//printf("%llu %s\n", count, prefix);
//puts(prefix);
return;
}
for (int i = 0; i < n; i++) {
if (!used[i]) {
used[i] = 1;
prefix[pos] = arr[i];
print_permutations(arr, n, used, prefix, pos + 1, k);
used[i] = 0;
}
}
}
int main(int argc, char *argv[]) {
int n = 26, k = 10;
char arr[27] = "abcdefghijklmnopqrstuvwxyz";
char used[27] = { 0 };
char perm[27];
unsigned long long expected_count;
clock_t start, elapsed;
if (argc >= 2)
k = strtol(argv[1], NULL, 0);
if (argc >= 3)
n = strtol(argv[2], NULL, 0);
start = clock();
print_permutations(arr, n, used, perm, 0, k);
elapsed = clock() - start;
expected_count = 1;
for (int i = n; i > n - k; i--)
expected_count *= i;
printf("%llu permutations, expected %llu, %.0f permutations per second\n",
count, expected_count, count / ((double)elapsed / CLOCKS_PER_SEC));
return 0;
}
Without output, this program enumerates 140 million combinations per second on my slow laptop, it would take 1.5 days to enumerate the 19275223968000 10-letter permutations from the 26-letter alphabet. It uses almost 100% of a single core, but the CPU is still 63% idle as I have a dual core hyper-threaded Intel Core i5 CPU. Using multiple threads should yield increased performance, but the program must be changed to no longer use a global variable count.
There are multiple reasons for your bad experience:
Your metric:
Your metric is fundamentally flawed. Peak-CPU% is an imprecise measurement for "how much work does my CPU do". Which normally isn't really what you're most interested in. You can inflate this number my doing more work (like starting another thread that doesn't contribute to the output at all).
Your proper metric would be items per second: How many different strings will be printed or written to a file per second. To measure that, start a test run with a smaller size (like k=4), and measure how long it takes.
Your problem: Your problem is hard. Printing or writing down all 26^10 ~1.4e+14 different words with exactly 10 letters will take some time. Even if you changed it to all permutations - which your program doesn't do - it's still ~1.9e13. The resulting file will be 1.4 petabytes - which is most likely more than your hard drive will accept. Also, if you used your CPU to 100% and used one thousand cycles for one word, it'd take 1.5 years. 1000 cycles are an upper bound, you most likely won't be faster that this while still printing your result, as printf usually takes around 1000 cycles to complete.
Your output: Writing to stdout is slow comapred to writing to a file, see https://stackoverflow.com/a/14574238/4838547.
Your program: There are issues with your program that could be a problem for your performance. However, they are dominated by the other problems stated here. With my setup, this program uses 93.6% of its runtime in printf. Therefore, optimizing this code won't yield satisfying results.
Before this gets accused of being a duplicate, I have looked everywhere on StackOverflow for this answer and have not been able to find something that can explain this to me, so please read the entirety first.
Suppose you need to write a function that takes an integer n and returns the sum of the positive integers from 1..n (I will use C).
int sum_of_integers(int n) {
int i, counter = 0;
for(i = 1; i <= n; i++)
counter += i;
return counter;
}
Obviously, this algorithm is in O(n) time, since the number of instructions it runs is proportional to the input size n.
However, consider this implementation, using the mathematical truth that 1+...+n = (n)(n+1)/2.
int sum_of_integers(int n) {
//Trying to avoid potential int overflow
//And take into account int division
if(n % 2 == 0)
return (n/2)*(n+1);
return ((n+1)/2)*n;
}
My Question: Since multiplication is technically in big-O > O(n), is the first implementation preferred? Is the second implementation considered to be in O(1) or not?
To me, because it does not matter what the size of n is for the second implementation since the same operations are being performed the same amount of times, I feel like it should be in O(1). On the other hand, the second implementation may actually be running more instructions based on the implementation of the multiplication operator.
Schoolbook multiplication takes time O(b^2) where b is the number of bits in the numbers, so using the formula n(n+1)/2 takes time O((log n)^2) which is much faster than O(n).
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
Recently I have gone through a problem like this
Write a program to read a sequence of N integers and print the number that
appears the maximum number of times in the sequence.
CONSTRAINTS
1 <= N <= 10000
The integers will be in the range [-100,100]
I have writen the following code:
main()
{int arr[201],max=0,maxelement,n,i,num;
int t;
scanf("%d",&n);
int *storenum=(int *)malloc(sizeof(int)*n);
for(i=0;i<201;i++)
{
arr[i]=0;
}
for(i=0;i<n;i++)
{
scanf("%d",&num);
storenum[i]=num;
if(num<=100 && num>=-100)
{
arr[num+100]=arr[num+100]+1;
}
}
for(i=0;i<n;i++)
{
int t=storenum[i]+100;
if(arr[t]>max)
{ maxelement=storenum[i];
max=arr[t];}
}
printf("\n\n%d",maxelement);
getch();
}
Now I think this code is not optimized one...I want to have a solution that would have less time and space complexity and better solution.
You don't have to iterate through all N items a second time, just iterate through the 201 counts looking for the largest.
Your code looks very close to optimal. It can be made only a little better given your constraints.
Use memset when you mean memset. Yes the compiler will pick it up 99% of the time, but why bother?
for(i=0;i<201;i++) arr[i]=0; // becomes
memset(arr, '\0', sizeof(arr))
The count for any given item can be no larger than 10000. That means it can be held in a ushort!
int arr[201]; // becomes
uint16_t arr[201];
Your second loop should go from for(i = -100; i <= 100; i++) instead, and just loop over arr.
Your code is butt-ugly. Use a consistent indentation, brace style, and don't cram 50 declarations on the same line. Spaces are okay between elements. arr should probably be named something meaningful, I like counts. It kind of looks like you're trying to use gnu style C, but K&R works too, really just pick any style guide and glance through it.
How do i make this code more efficient for larger numbers of the size 10^9. I don't think i can reduce the number of for loops in this.
#include <stdio.h>
int main(void) {
int e;
int t;
scanf("%d",&t);
for(e=0;e<t;e++){
int x,y;
scanf("%d",&x);
scanf("%d",&y);
int sum=0;
int j,k,i;
for(j=x;j<y;j++){
for(k=j+1;k<=y;k++){
int max=1;
for(i=2;i<=j;i++)
if((j%i==0)&&(k%i==0))
max=i;
sum+=max;
}
}
printf("%d",sum);
}
}
Euclid greatest common divisor algorithm is the most historical and probably the most efficient algorithm for calculating gcd, it may help you for reducing the number of for loops.
If the input size is very huge as you said and you can do some checks for some numbers manually before entering the loop and that may reduce the number loops by 10 times or so before entering.
(Even if in range checking for small numbers will be useful as 2*x == x*2)
Also may be multiples of 10's 5's and so on can be negated in the first loop.
Lot of better algorithm are available on net search a bit more if you cant come with your own
Try adding few comments in your code so it could be more understandable by lesser effort from the people helping you.
NOTE:- I didn't go through your code completely nor most off the people would do.ADD COMMENTS