Asymptotic Complexity of Small Functions - java

I am currently learning Java and basic algorithms by myself. I am struggling with the runtime complexity of loops and some recursive functions in terms of n (i.e O(n^2)). For example, for the following program:
public static void p(int n) {
if (n == 0) return; //n times??
int s = 0; // n times?
for (int k = 1; k <= Math.min(100, n); k = k + 1) {
//'k = 1' is n times, 'k<=mathmin' I am not sure on this one
//'k = k+1' is n*n or n*100?
s = s + 10; //n*n or n*100 times??
}
p(n - 1); //n-1 times?
//and also what about the overall asymptotic complexity?
//is it just simply adding together all the above number of times?
}
I commented my interpretations of the runtime of each step in the program but I am not sure whether I am on the right track or not. could someone help me please??

Related

Reducing the time of execution of the following code [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 5 years ago.
Improve this question
I am working on a problem. Out of 17 test cases 10 works fine and gives the result in less than a second but in 7 cases it is taking 2 seconds which are beyond the time limit. Following is the code
import java.util.*;
import java.io.*;
class TestClass
{
static PrintWriter wr = new PrintWriter(System.out);
public static void func1(int arr[], int n)
{
int temp = arr[0];
for (int jj = 0; jj < n; jj++)
{
if (jj == (n - 1))
arr[jj] = temp;
else
arr[jj] = arr[jj + 1];
}
}
public static void func2(int arr[], int n, int rt)
{
int count = 0;
for (int a = 0; a < n; a++)
{
for (int b = a; b < n; b++)
{
if (arr[a] > arr[b])
count++;
}
}
if (rt == (n - 1))
wr.print(count);
else
wr.print(count + " ");
}
public static void main(String args[]) throws Exception
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine().trim();
StringTokenizer st = new StringTokenizer(str);
int t = Integer.parseInt(st.nextToken());
for (int i = 0; i < t; i++) //for test cases
{
str = br.readLine().trim();
st = new StringTokenizer(str);
int n = Integer.parseInt(st.nextToken());
int arr[] = new int[n];
str = br.readLine().trim();
st = new StringTokenizer(str);
for (int j = 0; j < n; j++) //to take input of array for each test case
{
arr[j] = Integer.parseInt(st.nextToken());
}
for (int rt = 0; rt < n; rt++) //for number of times circular shifting of array is done
{
func1(arr, n); //circularly shifts the array by one position
func2(arr, n, rt); //prints the number of inversion counts
}
if (i != (t - 1))
wr.println();
}
wr.close();
br.close();
}
}
Can someone suggest how to optimize the code so that it takes less time in execution.
I know BufferReader and PrintWriter takes less time as compared to Scanner and System.out.print. I was using scanner and System.out.print earlier but changed it later in hope of getting less time but it didn't help. Also I did it earlier without the use of func1 and func2 and did all the operations in main only. The time in both the cases remains the same.
I am getting the currect output in all the cases so code is correct, I just need help in optimizing it.
The website you are using acquires questions from past programming competitions. I recognize this as a familiar problem
Like most optimization questions, the preferred steps are:
Do less.
Do the same in fewer instructions.
Don't use functions.
Use faster instructions.
In your case, you have an array, and you wish to rotate it a number of times, and then to process it from the rotated position.
Rotating an array is an incredibly expensive operation, because you typically need to copy every element in the array into a new location. What is worse for you is that you are doing it the simplest way, you are rotating the array one step for every step needing rotation.
So, if you have a 100 element array that needs rotated 45 steps, you would then have (3 copies per element swap) 100 * 45 * 3 copies to perform your rotation.
In the above example, a better approach would be to figure out a routine that rotates an array 45 elements at a time. There are a number of ways to do this. The easiest is to double the RAM requirements and just have two arrays
b[x] = a[(mod(x+45), a.length)]
An even faster "do less" would be to never rotate the array, but to perform the calculation in reverse. This is conceptually the function of the desired index in the rotated array to the actual index in the pre-rotated array. This avoids all copying, and the index numbers (by virtue of being heavily manipulated in the math processing unit) will already be stored in the CPU registers, which is the fastest RAM a computer has.
Note that once you have the starting index in the original array, you can then calculate the next index without going through the calculation again.
I might have read this problem a bit wrong; because, it is not written to highlight the problem being solved. However, the core principles above apply, and it will be up to you to apply them to the exact specifics of your programming challenge.
An example of a faster rotate that does less
public static void func1(int arr[], int shift) {
int offset = shift % arr.length;
int [] rotated = new int[arr.length];
// (arr.length - 1) is the last index, walk up till we need to copy from the head of arr
for (int index = 0; index < (arr.length - 1) - offset; index++) {
rotated[index] = arr[index+offset];
}
// copy the end of the array back into the beginning
for ( int index = (arr.length - 1) - offset; index < arr.length; index++ ) {
rotated[index] = (offset - ((arr.length - 1) - index) - 1);
}
System.arraycopy(rotated, 0, arr, 0, arr.length);
}
This copies the array into its rotated position in one pass, instead of doing a pass per index to be rotated.
The first rule of optimisation (having decided it is necessary) is to use a profiler. This counts how many times methods are invoked, and measures the accumulated time within each method, and gives you a report.
It doesn't matter if a method is slow if you only run it a few times. If you run it hundreds of thousands of times, you need to either make it faster, or run it fewer times.
If you're using a mainstream IDE, you already have a profiler. Read its documentation and use it.
The other first rule of optimisation is, if there's already literature about the problem you're trying to solve, read it. Most of us might have invented bubble-sort independently. Fewer of us would have come up with QuickSort, but it's a better solution.
It looks as if you're counting inversions in the array. Your implementation is about as efficient as you can get, given that naive approach.
for(int i=0; i< array.length; i++) {
int n1 = array[i];
for(int j=i+1; j< array.length; j++) {
n2 = array[j];
if(n1 > n2) {
count++;
}
}
}
For an array of length l this will take ( l - 1) + ( l - 2 ) ... 1 -- that's a triangular number, and grows proportionally to the square of l.
So for l=1000 you're doing ~500,000 comparisons. Then since you're repeating the count for all 1000 rotations of the array, that would be 500,000,000 comparisons, which is definitely the sort of number where things start taking a noticeable amount of time.
Googling for inversion count reveals a more sophisticated approach, which is to perform a merge sort, counting inversions as they are encountered.
Otherwise, we need to look for opportunities for huge numbers of loop iterations. A loop inside a loop makes for big numbers. A loop inside a loop inside another loop makes for even bigger numbers.
You have:
for (int i = 0; i < t; i++) {
// stuff removed
for (int rt = 0; rt < n; rt++) {
// snip
func2(arr, n, rt); //prints the number of inversion counts
}
// snip
}
public static void func2(int arr[], int n, int rt) {
// snip
for (int a = 0; a < n; a++) {
for (int b = a; b < n; b++) {
// stuff
}
}
// snip
}
That's four levels of looping. Look at the input values for your slow tests, and work out what n * n * n * t is -- that an indicator of how many times it'll do the work in the inner block.
We don't know what your algorithm is supposed to achieve. But think about whether you're doing the same thing twice in any of these loops.
It looks as if func1() is supposed to rotate an array. Have a look at System.arrayCopy() for moving whole chunks of array at a time. Most CPUs will do this in a single operation.

Do I have the right approach to finding the asymptotic complexity of the following Java functions?

Example1:
This I would say is O(log(n)), my reasoning is that if we choose a test n, n = 10, then 'i' would run: 0,2,4,6,8,10, so 'i' is behaving linearly not growing but just adding + 2 each time it iterates. And the method is also 'n' - dependent meaning that as n grows so does the method. So the first loop is O(n), then the second loop goes, if we choose n test to be n = 10, then after each iteration 'j' would run: 2,4,8,16,32,64 then can be thought of as 2^n which is a logarithmic function, so this loop is logarithmic. So computing this: O(n)*O(log(n)) = O(log(n))
for (i = 0; i < n; i = i + 2) {
for ( i = 1; j <= n; j = j*2) {
System.out.println("Hi");
}
}
Example2:
for this one if we choose n to be n = 10, then 'i' runs: 2,4,8,16,32 again can be rewritten as 2^n, this is logarithmic, O(log(n))
for (i = 1; i < n; i = i + i) {
System.out.println("Soda");
}
Example3:
for this one the first loop is n-dependent, the method changes as n grows, nothing fancy with 'i' it simply adds +1 so this is a n-dependent loop of O(n) for any inputted n.
if the number is even it runs the first nested loop, if the number is positive it runs the second nested loop. The first inner loop is the same complexity, O(n). and the last inner loop is (I think, despite n*n) O(n). So computing this we get: O(n)*O(n) = O(n^2) if the if-statement is true, and O(n)*O(n) if the if statement is false, so in the worst case it is O(n^2) because we multiply the two O(n)'s.
for (i = 0; i < n; i++) {
if (i % 2 == 0) {
for (j = 0; j < n; j++) {
System.out.println("Rawr");
}
} else {
for (j = 0; j < n*n; j++) {
System.out.println("Computer");
}
}
Have I made errors?
please explain
Thank you
Unfortunately, from the info you are giving, not much can be told. Are you looking for time complexity? spacetime complexity? runtime complexity?
In addition to this, the complexity of methods is actually only to be useful if the method is called more than once, which doesn't happen as you propose. If you don't know how the method is called, you will not be able to determine the complexity(either one of them) accurately.

What is an efficient method for adding thousands of numbers quickly?

I am attempting to solve a challenge, but I have hit a roadblock. I am a beginner programmer attempting to add tens of thousands of numbers. If I wait long enough, my program can easily yield the correct sum, however, I am looking for a more efficient method.
What is an efficient method for adding thousands of numbers quickly?
Side note: I have been reading about modular arithmetic, but I cannot quite wrap my head around it. Not sure if that could be useful for this situation.
I am attempting to get the sum of every prime number below 2 000 000. Here is my code so far:
public class Problem10 {
public static void main (String[] args) {
long sum = 0L;
for(long i = 1L; i < 2000000; i++) {
if(isPrimeNumber((int)i)) {
sum += i;
}
}
System.out.println(sum);
}
public static boolean isPrimeNumber(int i) {
int factors = 0;
int j = 1;
while (j <= i) {
if (i % j == 0) {
factors++;
}
j++;
}
return (factors == 2);
}
}
You can replace your isPrimeNumber() method with this to speed it up substantially.
public static boolean isPrimeNumber(int i) {
if (i==2) return true;
if (i==3) return true;
if (i%2==0) return false;
if (i%3==0) return false;
int j = 5;
int k = 2;
while (j * j <= i) {
if (i % j == 0) return false;
j += k ;
k = 6 - k;
}
return true;
}
This looks like homework, so I'm not going to give you the solution in code. The least you can do is code it yourself.
In your code, isPrimeNumber() is what's taking up most of the timeā€”if I had to guess, I would say 90-99% of it. What you can do to make it faster is implement the Sieve of Eratosthenes.
To start, you create an array that will hold all the prime numbers1. You should start it with a single value: 2. To find more prime numbers, iterate through every integer from 3 to the highest number you want. For each of those numbers, check if that number is divisible by any of the prime numbers in your array. If the next prime number in your array is greater than i / 2, you know that i is prime, and you can add it to your array.
After you have found all the prime numbers from 1 to n, the only way to sum them is by iterating through the array. That part cannot be optimized, but it will not take very long anyways.
1 There are two ways to do this. One is to just use an ArrayList or LinkedList, and add numbers as needed. The other is to create an array that is as large or larger than you need. As mentioned here, the number of primes equal to or less than n is less than (n / log(n)) * (1 + 1.2762 / log(n)), as long as n is greater than 598. If n is less than 598, you can just create an array of length 109.
In regards to the question in the title, "What is an efficient method for adding thousands of numbers quickly?", the only thing I can think of is multithreading. Create an array of all the numbers you want to sum, then have many threads sum different parts of the array. After that, sum all the results from each thread. This method will probably only be noticeably faster when summing huge amount of numbers, e.g. hundreds of thousands or millions.

Checking this Big O Analysis

I'm trying to learn Big O analysis, and I was wondering if someone could let me know if I'm doing it right with these two examples (and if I'm not, where did I go wrong?). I got the first one to be O(N^2) and the second to be O(N). My breakdown of how I got them is in the code below.
First example
public void sort(Integer[] v) {
//O(1)
if(v.length == 0)
return;
//O(N)*O(N)*O(1)
for(int i = 0; i < v.length; ++i)
{
for(int j = i + 1; j < v.length; ++j )
{
if(v[j].compareTo(v[i]) < 0)
{
Integer temp = v[i];
v[i] = v[j];
v[j] = v[i];
}
}
}
}
Second example
public void sort(Integer[] v){
TreeSet<Integer> t = new TreeSet<>();
//O(N)
for(int i = 0; i < v.length(); ++i)
{
t.add(v[i]);
}
int i = 0;
//O(N)
for(Integer value : temp)
{
v[i++] = v;
}
}
Thanks for the help!
You are right - the first is O(N^2) because you have one loop nested inside another, and the length of each depends on the input v. If v has length 2, you'll run those swaps 4 times. If v has length 8, they will execute 64 times.
The second is O(N) because you have iterate over your input, and your loop contain any iterative or expensive operations. The second is actually O(n log(n)) - see comments on the original post.
Your first example is O(N^2), you are right.
Your second example is not O(N), so you are not right.
It is O(N) * O(log N) + O(N)
O(N) first loop
O(log N) insert into set
O(N) second loop
Finally you have O(N * log N + N), take the higher value, so answer is O(N*log N)
Edited
By the way, Big O notation does not depend of programming language
It could helps

Sum of all prime numbers below 2 million

Problem 10 from Project Euler:
The program runs for smaller numbers and slows to a crawl in the hundred thousands.
At 2 million, an answer fails to show up even though the program seems like it is still running.
I'm trying to implement the Sieve of Eratosthenes. It is supposed to be very fast. What's wrong with my approach?
import java.util.ArrayList;
public class p010
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 2000000;
for (int i = 2; i < upper; i++)
{
primes.add(i);
}
int sum = 0;
for (int i = 0; i < primes.size(); i++)
{
if (isPrime(primes.get(i)))
{
for (int k = 2; k*primes.get(i) < upper; k++)
{
if (primes.contains(k*primes.get(i)))
{
primes.remove(primes.indexOf(k*primes.get(i)));
}
}
}
}
for (int i = 0; i < primes.size(); i++)
{
sum += primes.get(i);
}
System.out.println(sum);
}
public static boolean isPrime(int number)
{
boolean returnVal = true;
for (int i = 2; i <= Math.sqrt(number); i ++)
{
if (number % i == 0)
{
returnVal = false;
}
}
return returnVal;
}
}
You appear to be trying to implement the Sieve of Eratosthenes which should perform better that O(N^2) (In fact, Wikipedia says it is O(N log(log N)) ...).
The fundamental problem is your choice of data structure. You've chosen to represent the set of remaining prime candidates as an ArrayList of primes. This means that your test to see if a number is still in the set takes O(N) comparisons ... where N is the number of remaining primes. Then you are using ArrayList.remove(int) to remove the non-primes ... which is O(N) also.
That all adds up to making your Sieve implementation worse than O(N^2).
The solution is to replace the ArrayList<Integer> with an boolean[] where the positions (indexes) in the boolean array represent the numbers, and the value of the boolean says whether the number is prime / possibly prime, or not prime.
(There were other problems too that I didn't notice ... see the other answers.)
There are a few issues here. First, lets talk about the algorithm. Your isPrime method is actually the very thing that the sieve is designed to avoid. When you get to a number in the sieve, you already know it's prime, you don't need to test it. If it weren't prime, it would already have been eliminated as a factor of a lower number.
So, point 1:
You can eliminate the isPrime method altogether. It should never return false.
Then, there are implementation issues. primes.contains and primes.remove are problems. They run in linear time on an ArrayList, because they require checking each element or rewriting a large portion of the backing array.
Point 2:
Either mark values in place (use boolean[], or use some other more appropriate data structure.)
I typically use something like boolean primes = new boolean[upper+1], and define n to be included if !(primes[n]). (I just ignore elements 0 and 1 so I don't have to subtract indices.) To "remove" an element, I set it to true. You could also use something like TreeSet<Integer>, I suppose. Using boolean[], the method is near-instantaneous.
Point 3:
sum needs to be a long. The answer (roughly 1.429e11) is larger than the maximum value of an integer (2^31-1)
I can post working code if you like, but here's a test output, without spoilers:
public static void main(String[] args) {
long value;
long start;
long finish;
start = System.nanoTime();
value = arrayMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
start = System.nanoTime();
value = treeMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
}
output:
Using boolean[]
Value: 1.429e+11, time: 17 ms
Using TreeSet<Integer>
Value: 1.429e+11, time: 4869 ms
Edit:
Since spoilers are posted, here's my code:
public static long arrayMethod(int upper) {
boolean[] primes = new boolean[upper+1];
long sum = 0;
for (int i = 2; i <=upper; i++) {
if (!primes[i]) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes[k] = true;
}
}
}
return sum;
}
public static long treeMethod(int upper) {
TreeSet<Integer> primes = new TreeSet<Integer>();
for (int i = 2; i <= upper; i++) {
primes.add(i);
}
long sum = 0;
for (Integer i = 2; i != null; i=primes.higher(i)) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes.remove(k);
}
}
return sum;
}
Two things:
Your code is hard to follow. You have a list called "primes", that contains non prime numbers!
Also, you should strongly consider whether or not an array list is appropriate. In this case, a LinkedList would be much more efficient.
Why is this? An array list must constantly resize an array by: asking for new memory to create an array, then copying the old memory over in the newly created array. A Linked list would just resize the memory by changing a pointer. This is a lot quicker! However, I do not think that by making this change you can salvage your algorithm.
You should use an array list if you need to access the items non-sequentially, here, (with a suitable algorithm) you need to access the items sequentially.
Also, your algorithm is slow.Take the advice of SJuan76 (or gyrogearless), thanks sjuan76
The key to the efficiency of classic implementation of the sieve of Eratosthenes on modern CPUs is the direct (i.e. non-sequential) memory access. Fortunately, ArrayList<E> does implement RandomAccess.
Another key to the sieve's efficiency is its conflation of index and value, just like in integer sorting. Actually removing any number from the sequence destroys this ability to directly address without any computations. We must mark, not remove, any composite as we find them, so any numbers greater than it will remain in their places in the sequence.
ArrayList<Integer> can be used for that (except taking more memory than is strictly necessary, but for 2 million this is inconsequential).
So your code with a minimal edit fix (also changing sum to be long as others point out too), becomes
import java.util.ArrayList;
public class Main
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 5000;
primes.ensureCapacity(upper);
for (int i = 0; i < upper; i++) {
primes.add(i);
}
long sum = 0;
for (int i = 2; i <= upper / i; i++) {
if ( primes.get(i) > 0 ) {
for (int k = i*i; k < upper ; k+=i) {
primes.set(k, 0);
}
}
}
for (int i = 2; i < upper; i++) {
sum += primes.get(i);
}
System.out.println(sum);
}
}
Finds the result for 2000000 in half a second on Ideone. The projected run time for your original code there: between 10 and 400 hours (!).
To find rough estimates for the run time when faced with a slow code, you should always try to find out its empirical orders of growth: run it for some small size n1, then a bigger size n2, record the run times t1 and t2. If t ~ n^a, then a = log(t2/t1) / log(n2/n1).
For your original code the empirical orders of growth measured on 10k .. 20k .. 40k range of upper limit value N, are ~ N^1.7 .. N^1.9 .. N^2.1. For the fixed code it's faster than ~ N (in fact, it's ~ N^0.9 in the tested range 0.5 mln .. 1 mln .. 2 mln). The theoretical complexity is O(N log (log N)).
Your program is not the Sieve of Eratosthenes; the modulo operator gives it away. Your program will be O(n^2), where a proper Sieve of Eratosthenes is O(n log log n), which is essentially n. Here's my version; I'll leave it to you to translate to Java with appropriate numeric datatypes:
function sumPrimes(n)
sum := 0
sieve := makeArray(2..n, True)
for p from 2 to n step 1
if sieve[p]
sum := sum + p
for i from p * p to n step p
sieve[i] := False
return sum
If you're interested in programming with prime numbers, I modestly recommend this essay at my blog.

Categories

Resources