Jump to content

[CSE Help] Improve this algorithm?


Recommended Posts

Posted (edited)

Assignment:

827c9cdfeb3a69ff02fdaee9981499cf.png

 

Current Code:

public class Main {


	public static int RecursiveFirst(int n) throws IllegalArgumentException {
		if (n <= 0) {
			throw new IllegalArgumentException();
		} else if (n == 1) {
			return 1;
		} else if ((n % 2) == 0) {
			return 1 + RecursiveFirst(n / 2);
		} else {
			return 1 + RecursiveFirst(n - 1);
		}

	}

	public static void main(String Args[]) {
		int r = 0;
		for (int i = 1; i < 100; i++) {
			r = RecursiveFirst(i);
			System.out.println(r);
		}
	}

}

I have 0 idea on how to improve the run time of the algorithm so if anyone has ideas that would be nice.

 

Edit: what I came up with:

public static int RecursiveFirst(int n) throws IllegalArgumentException {
		ArrayList<Integer> res = new ArrayList<Integer>();
		res.add(n);
		while (n != 1) {
			if (n == 0) {
				throw new IllegalArgumentException();
			} else if (n % 2 == 0) {
				n = 1 + (n / 2);
				res.add(n);
			} else {
				n = 1 + (n - 1);
				res.add(n);
			}
			n /= 2;
		}
		return res.size();

	}

	public static void main(String Args[]) {
		for (int i = 1; i <= 10000; i++) {
			System.out.println(RecursiveFirst(i));
		}
	}
Edited by LoudPacks
Posted
	public static int rec1(int n) {
		c1++;
		if (n == 1) {
			return 1;
		} else if (n % 2 != 0) {
			return 1 + rec1(n - 1);
		} else {
			return 1 + rec1(n / 2);
		}
	}

	static int c2 = 0;

	public static int rec2(int n) {
		c2++;
		if (n == 1) {
			return 1;
		} else if (n % 2 != 0) {
			return 1 + rec2(n - 1);
		} else {
			int sum = 0;
			do {
				n = n / 2;
				sum++;
			} while (n % 2 == 0);
			if (n == 1)
				return 1 + sum;
			return 1 + sum + rec2(n - 1);
		}
	}

	public static void main(String[] args) {
		System.out.println(rec1(3128) + "; iterations: " + c1);
		System.out.println(rec2(3128) + "; iterations: " + c2);
	}

Output:

16; iterations: 16
16; iterations: 5

If you studied CPU architecture you should know every function call creates a new stackframe and new local variables with each call, taking up a lot of memory. 

 

SP = stack pointer register

BP = base pointer register

 

They are in your CPU, BP points to the base of the stack and SP points to the top stackframe aka the function call being executed by the CPU at a given moment.

Recursion is very inefficient memory-wise so the second function, which is still recursive but has an iterative component for powers of 2, is obviously superior.

  • Like 1
Posted (edited)
	public static int rec1(int n) {
		c1++;
		if (n == 1) {
			return 1;
		} else if (n % 2 != 0) {
			return 1 + rec1(n - 1);
		} else {
			return 1 + rec1(n / 2);
		}
	}

	static int c2 = 0;

	public static int rec2(int n) {
		c2++;
		if (n == 1) {
			return 1;
		} else if (n % 2 != 0) {
			return 1 + rec2(n - 1);
		} else {
			int sum = 0;
			do {
				n = n / 2;
				sum++;
			} while (n % 2 == 0);
			if (n == 1)
				return 1 + sum;
			return 1 + sum + rec2(n - 1);
		}
	}

	public static void main(String[] args) {
		System.out.println(rec1(3128) + "; iterations: " + c1);
		System.out.println(rec2(3128) + "; iterations: " + c2);
	}

Output:

16; iterations: 16
16; iterations: 5

If you studied CPU architecture you should know every function call creates a new stackframe and new local variables with each call, taking up a lot of memory. 

 

SP = stack pointer register

BP = base pointer register

 

They are in your CPU, BP points to the base of the stack and SP points to the top stackframe aka the function call being executed by the CPU at a given moment.

Recursion is very inefficient memory-wise so the second function, which is still recursive but has an iterative component for powers of 2, is obviously superior.

 

 

Go with the second solution first, then do the first one if necessary.

 

Although Token is right, this is like trying to make a gun shot wound better by putting a bandage on it. It may save some runtime but the real improvement is the solution below.

 

Optimizing from a recusive to an iterative approach will probably cut your runtime by several times, but using the below solution can cut your runtime by an entire order of magnitude or more (ie. from n^2 to n).

 

You should be caching the results of previously computed answers and using it from cache (called Dynamic programming), which is what Solzhenitsyn is suggesting.

 

What if you save the return value of each recursive call in a map, and then check to see if that value has been called previously at the start of each call? 

 

Edit:

 

1DIt4af.png

try replacing

n % 2 == 0

with

(n & 1) == 0

From what I understand, modulo is a fairly expensive operation.

Tho I reckon jabba mite optimize it by itself..

Pretty sure it does. Tried it out before and I think the runtime's about the same

Edited by Ericthecmh
  • Like 1
  • 2 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...