Interesting thus complex recursive Big-O Calculation - Specific examples - java

I am totally a beginner learning a subject called Algorithms and Data Structures and got to the part about Big-O Notation. I have read many different materials writing about this but most of them just show examples of calculation of simple cases.
The assignment for this topic has some really interesting complex examples with recursion calling each other and for loops, while loops, etc...which I could not figure out and need help on calculating. I really appreciate any help and explanation, to understand deeply about this.
Also:
Ex No.3: I don't understand what the meaning of return "0xCAFE + 0xBABE + s"? I couldn't see it appear anywhere in the method, really strange to me.
Ex No.4: At first I thought these are different examples but I notice in method g has a call for method f so it should be in one example, is my assumption correct?
1.
long c(int x) {
if (x <= 1) {
return 1;
} else {
long s = 0;
for (int i = 1; i < x; i++) {
s = s + c(x - 1);
}
return s;
}
}
2.
long d(int x) {
long s = -x * x;
while (s <= x * x * x) {
s++;
}
for (long i = s * x; i > 0; i--) {
s--;
}
return s;
}
3.
double e(long x, long y) {
double s = 0_0;
for (int i = 1; i <= x; i *= 2) {
for (double j = x; j >= 1; j /= 3) {
for (int k = 0; k < y; k += 4) {
s++;
}
}
}
return 0xCAFE + 0xBABE + s;
}
4.Calculate each f & g
long f(int x, int y) {
if (x <= 0) {
return y;
} else {
return f(x - 1, 2 * y);
}
}
double g(int x, int y) {
double s = 0.0D;
for (long i = f(x, y); i >= 0; i--) {
s++;
}
return s;
}
5.
char h(int x) {
char h = 'h';
for (long i = h; i-- > ++i; x--) {
h++;
}
return h;
}

Big-O is just a way to measure the Runtime it takes for algorithms to complete.
You should look at the base of how to understand this before you try to understand these examples because they're rather complex.
A small example: O(n)
public void o(int n, int i) {
if (i == n)
return;
o(n, i++);
}
This resembles a for loop,
for (int i = 0; i <= n; i++)
In example 3:
return 0xCAFE + 0xBABE + s;
Is arbitrary, it looks like it doesn't represent anything and 's' will be the big O value for the methods complexity through its loops,
for (int i = 1; i <= x; i *= 2) { // BigO = 2 ^ i
for (double j = x; j >= 1; j /= 3) { // BigO = (2 ^i) / 3
for (int k = 0; k < y; k += 4) { //BigO = y / 4

Related

perfect squares leetcode - recursive solution with memoization

Trying to solve this problem with recursion and memoization but for input 7168 I'm getting wrong answer.
public int numSquares(int n) {
Map<Integer, Integer> memo = new HashMap();
List<Integer> list = fillSquares(n, memo);
if (list == null)
return 1;
return helper(list.size()-1, list, n, memo);
}
private int helper(int index, List<Integer> list, int left, Map<Integer, Integer> memo) {
if (left == 0)
return 0;
if (left < 0 || index < 0)
return Integer.MAX_VALUE-1;
if (memo.containsKey(left)) {
return memo.get(left);
}
int d1 = 1+helper(index, list, left-list.get(index), memo);
int d2 = 1+helper(index-1, list, left-list.get(index), memo);
int d3 = helper(index-1, list, left, memo);
int d = Math.min(Math.min(d1,d2), d3);
memo.put(left, d);
return d;
}
private List<Integer> fillSquares(int n, Map<Integer, Integer> memo) {
int curr = 1;
List<Integer> list = new ArrayList();
int d = (int)Math.pow(curr, 2);
while (d < n) {
list.add(d);
memo.put(d, 1);
curr++;
d = (int)Math.pow(curr, 2);
}
if (d == n)
return null;
return list;
}
I'm calling like this:
numSquares(7168)
All test cases pass (even complex cases), but this one fails. I suspect something is wrong with my memoization but cannot pinpoint what exactly. Any help will be appreciated.
You have the memoization keyed by the value to be attained, but this does not take into account the value of index, which actually puts restrictions on which powers you can use to attain that value. That means that if (in the extreme case) index is 0, you can only reduce what is left with one square (1²), which rarely is the optimal way to form that number. So in a first instance memo.set() will register a non-optimal number of squares, which later will get updated by other recursive calls which are pending in the recursion tree.
If you add some conditional debugging code, you'll see that map.set is called for the same value of left multiple times, and with differing values. This is not good, because that means the if (memo.has(left)) block will execute for cases where that value is not guaranteed to be optimal (yet).
You could solve this by incorporating the index in your memoization key. This increases the space used for memoization, but it will work. I assume you can work this out.
But according to Lagrange's four square theorem every natural number can be written as the sum of at most four squares, so the returned value should never be 5 or more. You can shortcut the recursion when you get passed that number of terms. This reduces the benefit of using memoization.
Finally, there is a mistake in fillSquares: it should add n itself also when it is a perfect square, otherwise you'll not find solutions that should return 1.
Not sure about your bug, here is a short dynamic programming Solution:
Java
public class Solution {
public static final int numSquares(
final int n
) {
int[] dp = new int[n + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for (int i = 1; i <= n; i++) {
int j = 1;
int min = Integer.MAX_VALUE;
while (i - j * j >= 0) {
min = Math.min(min, dp[i - j * j] + 1);
++j;
}
dp[i] = min;
}
return dp[n];
}
}
C++
// Most of headers are already included;
// Can be removed;
#include <iostream>
#include <cstdint>
#include <vector>
#include <algorithm>
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
#define MAX INT_MAX
using ValueType = std::uint_fast32_t;
struct Solution {
static const int numSquares(
const int n
) {
if (n < 1) {
return 0;
}
static std::vector<ValueType> count_perfect_squares{0};
while (std::size(count_perfect_squares) <= n) {
const ValueType len = std::size(count_perfect_squares);
ValueType count_squares = MAX;
for (ValueType index = 1; index * index <= len; ++index) {
count_squares = std::min(count_squares, 1 + count_perfect_squares[len - index * index]);
}
count_perfect_squares.emplace_back(count_squares);
}
return count_perfect_squares[n];
}
};
int main() {
std::cout << std::to_string(Solution().numSquares(12) == 3) << "\n";
return 0;
}
Python
Here we can simply use lru_cache:
class Solution:
dp = [0]
#functools.lru_cache
def numSquares(self, n):
dp = self.dp
while len(dp) <= n:
dp += min(dp[-i * i] for i in range(1, int(len(dp) ** 0.5 + 1))) + 1,
return dp[n]
Here are LeetCode's official solutions with comments:
Java: DP
class Solution {
public int numSquares(int n) {
int dp[] = new int[n + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
// bottom case
dp[0] = 0;
// pre-calculate the square numbers.
int max_square_index = (int) Math.sqrt(n) + 1;
int square_nums[] = new int[max_square_index];
for (int i = 1; i < max_square_index; ++i) {
square_nums[i] = i * i;
}
for (int i = 1; i <= n; ++i) {
for (int s = 1; s < max_square_index; ++s) {
if (i < square_nums[s])
break;
dp[i] = Math.min(dp[i], dp[i - square_nums[s]] + 1);
}
}
return dp[n];
}
}
Java: Greedy
class Solution {
Set<Integer> square_nums = new HashSet<Integer>();
protected boolean is_divided_by(int n, int count) {
if (count == 1) {
return square_nums.contains(n);
}
for (Integer square : square_nums) {
if (is_divided_by(n - square, count - 1)) {
return true;
}
}
return false;
}
public int numSquares(int n) {
this.square_nums.clear();
for (int i = 1; i * i <= n; ++i) {
this.square_nums.add(i * i);
}
int count = 1;
for (; count <= n; ++count) {
if (is_divided_by(n, count))
return count;
}
return count;
}
}
Java: Breadth First Search
class Solution {
public int numSquares(int n) {
ArrayList<Integer> square_nums = new ArrayList<Integer>();
for (int i = 1; i * i <= n; ++i) {
square_nums.add(i * i);
}
Set<Integer> queue = new HashSet<Integer>();
queue.add(n);
int level = 0;
while (queue.size() > 0) {
level += 1;
Set<Integer> next_queue = new HashSet<Integer>();
for (Integer remainder : queue) {
for (Integer square : square_nums) {
if (remainder.equals(square)) {
return level;
} else if (remainder < square) {
break;
} else {
next_queue.add(remainder - square);
}
}
}
queue = next_queue;
}
return level;
}
}
Java: Most efficient solution using math
Runtime: O(N ^ 0.5)
Memory: O(1)
class Solution {
protected boolean isSquare(int n) {
int sq = (int) Math.sqrt(n);
return n == sq * sq;
}
public int numSquares(int n) {
// four-square and three-square theorems.
while (n % 4 == 0)
n /= 4;
if (n % 8 == 7)
return 4;
if (this.isSquare(n))
return 1;
// enumeration to check if the number can be decomposed into sum of two squares.
for (int i = 1; i * i <= n; ++i) {
if (this.isSquare(n - i * i))
return 2;
}
// bottom case of three-square theorem.
return 3;
}
}

Recursive Longest Common Substring (LCS) problem optmization

I have the following code in Java:
public static int Secret(String a, String b, int x, int y){
if ((x == -1) || (y == -1)) {
return 0;
}
if (a.charAt(x) == b.charAt(y)) {
return Secret(a, b, x-1, y-1) + 1;
} else {
int left = Secret(a, b, x, y-1);
int up = Secret(a, b, x-1, y);
if (left > up) {
return left ;
} else {
return up;
}
}
return -1;
}
This code looks like the Longest Common Substring Problem but with a garbage complexity. I am supposed to opmitize it to O(mn) (space and time).
I tried following the algorithm on the wikipedia (https://en.wikipedia.org/wiki/Longest_common_substring_problem), that is O(mn) (space and time).
public static int iterativeLCS(String a, String b, int x, int y) {
int[][] L = new int[x+1][y+1];
int z = 0;
for (int i = 0; i <= x; i++) {
for (int j = 0; j <= y; j++) {
if (a.charAt(i) == b.charAt(j)) {
if ((i == 0) || (j == 0)) {
L[i][j] = 1;
} else {
L[i][j] = L[i-1][j-1] + 1;
}
if (L[i][j] > z) {
z = L[i][j];
}
} else {
L[i][j] = 0;
}
}
}
}
But the results don't match for some inputs. For example:
xalmandriatico
talcolaritriom
13
13
Expected output (using the recursive alg): 8
Actual output (using the iterative alg from wikipedia): 2
Any idea on what I have to do?

Array OutOfBounds Exception with MergeSort

I am doing a simple MergeSort implementation taking it form a pseudocode. I use Java Generics for that purpose. However I get such exception on the last element in the first for-loop. I have already made some changes (hope for the better) but still this one inevitably comes up. Why is that so?
private Comparable[] mergesort(Comparable[] elements, int l, int r) {
if(l < r){
int m = (l + r - 1)/2;
mergesort(elements, l, m);
mergesort(elements, m + 1, r);
int i = l;
int j = m + 1;
int k = l;
Comparable[] elements1 = (Comparable[])new Comparable[l + r]; //changed from [l + r - 1] and in the function caller also mergesort(elements, elements.length - elements.length, elements.length - 1)
while(i <= m && j <= r){
if(elements[i].compareTo(elements[j]) <= 0 ){
elements1[k] = elements[i];
i++;
} else {
elements1[k] = elements[j];
j++;
}
k++;
}
for(int h = i; i <= m; h++){
elements[k + (h - i)] = elements[h];
//ArrayIndexOutOfBoundsException: 4(the length of the input array)
}
for(int h = j; h <= k - 1; h++){
elements[h] = elements1[h];
}
}
return elements;
}
While your code is hard to read, I think you are comparing the wrong value.
for(int h = i; i <= m; h++){
^
should be h
elements[k + (h - i)] = elements[h];
//ArrayIndexOutOfBoundsException: 4(the length of the input array)
}
You use:
for(int h = i; i <= m; h++) {
elements[k + (h - i)] = elements[h];
}
You always increase h but compare i <= m. Since you never change i you have an endless loop.

Finding Two Bases Which When Converted to Base 10 Are Equal

I have a very challenging problem here today. I cannot think of a way to solve it.
Given 6 numbers as input: a1, a2, a3, b1, b2, b3, find 2 numbers X and Y such that a1 * x^2 + a2 ^ x + a3 = b1 * y^2 + b2 * y + b3. X and Y must be between 10 and 15000 inclusive.
What I have tried:
I have tried all X values from 10-15000 and all Y values from 10-15000, and checked if they satisfied the equation. However, this method is extremely slow. Does anyone have a faster solution? Thanks.
My Bad Code:
for (int i = 0; i < k; i++) {
int a, b;
cin >> a >> b;
for (int i = 10; i <= 15000; i++) {
for (int j = 10; j <= 15000; j++) {
if (conv(a, i) == conv(b, j)) {
cout << i << " " << j << endl;
j = 20000;
i = 20000;
}
}
}
}
long long conv(int x, int b) {
long long ans = 0;
int count = 0;
while (x) {
int y = x % 10;
ans += y * poww(b, count);
count++;
x /= 10;
}
return ans;
}
long long poww(int x, int y) {
long long ans = 1;
while (y != 0) {
ans *= x;
y--;
}
return ans;
}
I thought this might be a good occassion to exercise writing some Java code and came up with the following solution. On my system it gives the solution to the two numbers 419 and 792 (as you wrote in an earlier edit of your question the result should be Base X: 47 Base Y: 35) in 1 ms.
The code just uses some smart brute force :).
See it running online.
public class TwoBases {
public static void main(String[] args) {
long beg = System.nanoTime();
solve(419, 792);
System.out.println("Time needed to calculate: "+(System.nanoTime()-beg)/1000000.0 + "ms");
}
public static void solve(int a, int b) {
int[] aDigits = new int[3];
int[] bDigits = new int[3];
for (int i = 0; i < 3; i++) {
aDigits[2 - i] = (a / (int) Math.pow(10, i)) % 10;
bDigits[2 - i] = (b / (int) Math.pow(10, i)) % 10;
}
for (int x = 10; x <= 15000; x++) {
int numBaseX = digitsToBase10(aDigits, x);
int y = 10;
while (y <= 15000) {
int numBaseY = digitsToBase10(bDigits, y);
if (numBaseX == numBaseY) {
System.out.println("Base X: " + x + " Base Y: " + y);
return;
} else if (numBaseY > numBaseX) {
break;
} else {
y++;
}
}
}
System.out.println("Nothing found");
}
public static int digitsToBase10(int[] digits, int b) {
int res = 0;
for (int i = 0; i < digits.length; i++) {
res += digits[i] * (int) Math.pow(b, digits.length - 1 - i);
}
return res;
}
}

Algorithm Variation of Naive

Guys I am doing polynomial evaluation and using algorithms such as Naive, Horner and FFT
now there is one statement in my question that states.
Run a variation of the naïve algorithm, where the exponentiation
is performed by repeated squaring, a decrease-by-half algorithm
I do not understand it, My current Naive algorithm is:
public Complex naive(Polynomial poly, Complex x) {
Complex p = new Complex();
for (int i = poly.getCoef().length - 1; i >= 0; i--) {
Complex power = new Complex(1, 0);
for (int j = 1; j <= i; j++) {
power = power.multiply(x);
}
p = p.add(poly.getCoef()[i].multiply(power));
multiplyCountNaive++;
}
return p;
}
Kindly explain what needs to be modified.
Thank you
I got it it was supposed to be like this
public Complex naive2(Polynomial poly, Complex x) {
Complex p = new Complex();
for (int i = poly.getCoef().length - 1; i >= 0; i--) {
p = p.add(poly.getCoef()[i].multiply(expo(x, i)));
multiplyCountNaive2++;
}
return p;
}
private Complex expo(Complex a, int b) {
if (b == 0) {
return new Complex(1, 0);
} else if (b == 1) {
return a;
}
if (b % 2 == 0) {
return expo(a.multiply(a), b / 2);
} else {
return a.multiply(expo(a.multiply(a), (b - 1) / 2));
}
}

Categories