Implement floored square root using binary search - java

Okay, so I've been working on this for some time now and I know that my logic is correct, however, I can't seem to produce the correct floored square root of a positive number.
public int mySqrt(int x) {
if(x < 2) return x;
double lowerBound = 0.0, upperBound = x, midPoint = 0.0;
while(lowerBound <= upperBound) {
midPoint = lowerBound + (upperBound - lowerBound) / 2;
double square = Math.pow(midPoint, 2);
if(Double.compare(square, x) < 0) lowerBound = midPoint + 1;
else if(Double.compare(square, x) > 0) upperBound = midPoint - 1;
else return (int) midPoint;
}
return (int) midPoint;
}
A test case I fail, for example, is for x = 2: it should return 1 but I return 2. which doesn't make sense because I clearly take a midPoint first. Is the logic to go left or right incorrect?

Since you are performing binary search on a double value, you should set a tolerance, and stop looping once the difference between high and low drops below that tolerance (a standard tolerance would usually be 1e-6). You also should set low = mid or high = mid instead of adding or subtracting one, as you are not binary searching over int values. See the below code in action here.
private static final double TOLERANCE = 1e-10;
public int mySqrt(int x) {
if (x < 2)
return x;
double lowerBound = 0.0, upperBound = x, midPoint = 0.0;
while (upperBound - lowerBound >= TOLERANCE) {
midPoint = lowerBound + (upperBound - lowerBound) / 2;
double square = Math.pow(midPoint, 2);
if (Double.compare(square, x) < 0)
lowerBound = midPoint;
else if (Double.compare(square, x) > 0)
upperBound = midPoint;
else
return (int) midPoint;
}
return (int) midPoint;
}
If you never anticipate needing more precision, you can just binary search using ints. See the below code in action here.
public int mySqrt(int x) {
if (x < 2)
return x;
int low = 0, high = x;
while (low < high - 1) {
final int mid = low + high >>> 1;
if (mid <= x / mid) {
low = mid;
} else {
high = mid;
}
}
return low;
}

You shouldn't be using any double math for this. You have to do a lot more iterations to get an accurate double value, and then you just throw it away. You should use an integer solution like this:
int mySqrt(int x) {
if (x<2) {
return x;
}
int minval=1, maxval=x/2;
while(minval < maxval) {
// rounding up here means we never choose minval
int test = minval + (maxval - minval + 1)/2;
// testing this way avoids a multiply that could overflow
if (x/test < test) {
//too high
maxval = test-1;
} else {
//not too high
minval = test;
}
}
return minval;
}

Since the input is int and the result is int, this shouldn't involve floating point arithmetic at all.
public static int mySqrt(int i) {
if (i < 2)
return i;
int lower = 0, upper = i;
do {
int guess = lower + (upper - lower) / 2;
if(guess <= i / guess) {
lower = guess;
} else {
upper = guess;
}
} while ((upper - lower) > 1);
return lower;
}

Related

Finding the square root of a number by using binary search

I tried using binary search to find the square root of an integer, but some how I couldn't pass a few test cases.
I was able to pass mySqrt(4) = 2, but I wasn't able to pass mySqrt(2147395599)
Any ideas on where I messed up?
public static int mySqrt(int x) {
int left = 0;
int right = x;
if(x < 2){
return x;
}
while(left < right){
int mid = left + ((right - left) / 2);
if(mid * mid == x){
return mid;
}
else if(mid * mid < x){
left = mid + 1;
}
else{
right = mid;
}
}
return left - 1;
}
Because mid * mid will overflow. You should use long to avoid the overflow. Then cast it back to int when you return the result.
Try this code
public static int mySqrt(int x) {
long left = 0;
long right = x;
if(x < 2){
return x;
}
while(left < right){
long mid = left + ((right - left) / 2);
if(mid * mid == x){
return (int)mid;
}
else if(mid * mid < x){
left = mid + 1;
}
else{
right = mid;
}
}
return (int)(left - 1);
}
Here is a version which handles doubles.
It is recursive.
It computes until a given precision is reached.
for (int i = 2; i < 10; i++) {
System.out.println("sqrt("+i+") = " + sqrt(i));
}
Prints
sqrt(2) = 1.414213562373095
sqrt(3) = 1.7320508075688772
sqrt(4) = 2.0
sqrt(5) = 2.23606797749979
sqrt(6) = 2.449489742783178
sqrt(7) = 2.6457513110645907
sqrt(8) = 2.82842712474619
sqrt(9) = 3.0
public static double sqrt(double i) {
return bsqrt(i, 0, i, 0);
}
static double prec = 10E-200;
private static double abs(double d) {
return d < 0 ? -d : d;
}
private static double bsqrt(double i, double low, double high,
double last) {
double mid = (high + low) / 2;
double d = last - mid;
if (d < 0) {
d = -d;
}
if (d < prec) {
return mid;
}
double sqr = mid * mid;
if (sqr < i) {
return bsqrt(i, mid, high, mid);
} else {
return bsqrt(i, low, mid, mid);
}
}
But a better way is to use Newton's method.
static double prec = 10E-15;
public static double newtons(double i) {
// initial guess
double x = i / 2;
double d = i;
double nx = 0;
while (abs(d) > prec) {
nx = x - (x*x - i)/(2*x);
d = nx - x;
x = nx;
}
return nx;
}
This is like a binary search
The following code computes the [integer] square root of a number. If the number is not a
perfect square (there is no integer square root) , then it returns -1. It does this by successive
guessing. If n is 100, it first guesses SO. Too high? Try something lower - halfway between 1
and SO. What is its runtime?
int sqrt(int n) {
return sqrt_helper(n, 1, n);
}
int sqrt_helper(int n, int min, int max) {
if (max < min) return -1; // no square root
int guess = (min + max) / 2·,
if (guess *guess == n) { // found it!
return guess;
}else if (guess * guess < n) { II too low
return sqrt_helper(n, guess + 1, max); // try higher
} else { // too high
return sqrt_helper(n, min, guess - l); //try lower
}
}
credits: cracking the coding interview

Writing a Power method with a minimum amount of steps n(log)N

I'm trying to write the Power method and now the question is how to write it in a better way. Here is my code, I've verified it works well for all the Power conditions, however I can't think on a better solution.
The real issue is how I express the power with a Negative exponent in O(log)n condition
public static double raiseToPowerIterative(double x, int n) {
double sol = 1;
if (n == 0) {
return 1;
}
if (n > 0) {
for (int i = 1; i <= n;i++) {
sol *= x;
}
return sol;
} else if (n < 0){
for (int i = -1; i >= n; i--) {
sol /= x;
}
}
return sol;
}
You need some additional math rules, you are using:
xn = x.x. ... .x (n times)
With O(n)
However
x2k = xk . xk
x2k+1 = x.x2k
Which can deliver O(²log n), as you are doing exponents n → n/2 → n/2/2 → ... → 1.
Where you only need to calculate xk once.
A recursive solution might be easiest; calling oneself for xk.
Your solution:
That does not work, I think. As probably past limit date of the work.
Wrong:
double power(double x, int n) {
if (n == 0) {
return 1;
}
boolean negative = ( n < 0 );
if (negative) {
n = -n;
}
double sol = 1;
while (n > 0) {
if (n%2 == 1) {
sol *= x;
}
x *= x;
n /= 2;
}
if (negative) {
return 1 / sol;
}
return sol;
}
Correct recursive:
double power(double x, int n) {
if (n == 0) {
return 1;
} else if (n < 0) {
return 1 / power(x, -n);
}
double sqrPower = power(x, n/2);
int p = sqrPower * sqrPower;
if (n % 1 == 1) {
p *= x;
}
return p;
}
That is, one has to do something with the result of the recursive call.
For an iterative loop one would need a stack to reconstruct the result.
Correct iterative:
double power(double x, int n) {
if (n == 0) {
return 1;
}
boolean invert = n < 0;
if (invert) {
n = -n;
}
Stack<Double> factors = new Stack<>();
while (n > 0) {
factors.push(n % 1 == 1 ? x : 1.0);
n /= 2;
}
double sol = 1;
while (!factors.empty()) {
double f = factors.pop();
sol = sol * sol * f;
}
return invert ? 1/sol : sol;
}

Implementation of sinus function | Calculation Time

Can anybody tell me why this code doesn't work? I'm trying to implement a sinus-function with a given limitation of precision (by the error-variable).
Certain details about the calculation are given here: https://en.wikipedia.org/wiki/Taylor_series
berechnePot calculates the potence
berechneFak calculates the faculty
berechneVZW calculates the pre sign (plus or minus)
I don't get the point, why the function is calculating that slow.
public class Sinus {
public static double sinus(double x) {
double error = 0.001;
double summand = 0;
double sum = 0;
int k = 0;
do {
double pot = 0;
double fak = 0;
pot = berechnePot(x, k);
fak = berechneFak(k);
summand = pot / fak;
berechneVZW(summand, k);
sum += summand;
k++;
} while (abs(summand) > error);
return sum;
}
public static double abs(double value) {
if (value < 0) return -value;
else return value;
}
public static double berechneVZW(double value, int k) {
if (k % 2 == 0) {
return value;
} else {
return value *= (-1);
}
}
public static double berechnePot(double x, double k) {
double pot = 0;
pot += x;
for (int i = 0; i <= k; i++) {
pot *= (x * x);
}
return pot;
}
public static double berechneFak(int k) {
double fak = 1;
if (k == 0) {
return 1;
} else {
for (int i = 0; i <= k; k++) {
fak *= (2 * i + 1);
}
}
return fak;
}
}
Finally i got to the right solution..
I hope that the new structure helps you to better understand my implementation better.
Thanks for all your help!
public class Sinus {
public static double sinus(double x) {
double error = 0.00001;
double summand = 0;
double result = 0;
double fak = 0;
int k = 0;
do {
double pot = 0;
pot = calcNumeratorPotency(x, k);
fak = calcDenumeratorFaculty(k);
summand = pot / fak;
summand = definePreSign(summand, k);
result += summand;
k++;
} while (absoluteValue(summand) > error);
return result;
}
public static double absoluteValue(double value) {
if (value < 0)
return -value;
else
return value;
}
public static double definePreSign(double value, int k) {
if (k % 2 == 0) {
return value;
} else {
return value * (-1);
}
}
public static double calcNumeratorPotency(double x, double k) {
double pot = x;
for (int i = 0; i < 2 * k; i++) {
pot *= x;
}
return pot;
}
public static double calcDenumeratorFaculty(int k) {
double fak = 1;
if (k == 0) {
return 1;
} else {
for (int i = 1; i <= (2 * k + 1); i++) {
fak *= i;
}
}
return fak;
}
You seem to have worked with another language before. Java works a bit different than you seem to expect.
for (int i = 0; i <= k; k++) {
fak *= (2 * i + 1);
}
This particular loop is definetly not working as expected. You increment k, but iis supposed to grow? Might be you want to write:
for (int i = 0; i <= k; i++) {
fak *= (2 * i + 1);
}
Because the loop counts k, instead of i, it continues, until k overruns the integer-range and becomes Integer.MIN_VALUE. At that point, your loop finally terminates. :)
On a completely different note, and meant as constructive critique: You might want to take a look at the default Java-Style-Guide (https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md)
Small excerpt:
// Bad.
// - This offers poor visual separation of operations.
int foo=a+b+1;
// Good.
int foo = a + b + 1;
Spaces between identifiers and operators are very, very helpful, operating with numbers, but also with a lot of different stuff.
It is not quite clear to me what you are calculating in berechnePot and berechnePot. They seem to be the numerators and denominators, judging from the context.
Running your code with a debugger, I can see that summand is calculated very wrongly, and decreases very slowly. This is the reason why it takes so long.
The Math class provides a pow method, so you don't really need to use a for loop to calculate powers. I think you might be overcomplicating this a bit. I would write a getSummand method and a factorial method:
private static double getSummand(double x, int k) {
int power = k * 2 + 1;
double numerator = Math.pow(x, power);
double denominator = factorial(power);
int sign = k % 2 == 1 ? -1 : 1;
return numerator / denominator * sign;
}
private static double factorial(int x) {
double result = 1;
for (int i = 1; i <= x; i++) {
result *= i;
}
return result;
}
And use them like this:
public static double sinus(double x) {
double error = 0.001;
double summand = 0;
double sum = 0;
int k = 0;
do {
summand = getSummand(x, k);
sum += summand;
k++;
} while (Math.abs(summand) > error);
return sum;
}
If this is an assignment and you are not allowed to use anything from the Math class, you could write your own pow method like this:
private static double pow(double x, int p) {
double result = 1;
for (int i = 0 ; i < p ; i++) {
result *= x;
}
return result;
}
In your berechneFak() function you have a loop inside the else clause. You do increment k but not i so i <= k is always true. Try looking at it in a debugger. This is the loop that is slowing it down.
So every time k will count up to the max integer value of 2,147,483,647 in single increments and then overflow to a negative value at which point the loop will end.
Just to clarify: I did not look at if the math is correct but just at why the program is slow.

Loss of accuracy with floats

I am trying to compute a summation of float numbers. All small numbers output properly, when I use very large numbers as inputs, the output is always off by a few integers. For example, H = 5764801 W = 1679616, on paper, works out as 335923 30275911. In my program though, 335923 30275908 is printed instead. Here is the code:
public void printOutput(int H, int W) // The inputs
{
if(H == 1 && W == 1)
{
System.out.println(0 + " " + 1);
return;
}
List<Integer> pfw = primeFactors(W);
int y = 1;
while(H != (int) (Math.pow(Math.pow(W, 1f/y) + 1f, y))) y++;
final float N = findWholeNumber(pfw);
float height = 0;
for(int x = 1; x <= y + 1; x++)
{
height += (float) (W * Math.pow((N + 1f) / N, x-1f) + 1e-8); //Here is the summation
}
float cats = 1;
for(int x = 2; x <= y + 1; x++)
cats += (float) (Math.pow(N, x-1));
int notWorking = (int) (cats - W);
System.out.println(notWorking + " " + (int)height); //Outputs printing
}
private int findWholeNumber(List<Integer> factors)
{
List<Integer> common = new ArrayList<Integer>();
for(int i = 0; i < factors.size(); i++)
{
if(common.contains(factors.get(i))) continue;
common.add(factors.get(i));
}
int num = common.get(0);
for(int i = 1; i < common.size(); i++)
num *= common.get(i);
return num;
}
private List<Integer> primeFactors(int num)
{
List<Integer> pf = new ArrayList<Integer>();
if(num == 1)
{
pf.add(1);
return pf;
}
for(int j = 2; j <= num; j++)
while(num % j == 0) // is prime
{
pf.add(j);
num /= j;
}
return pf;
}
}
Floating point numbers have a limited precision as mantissa has a limited width.
You could try double for your case which precision is more (as its mantissa is wider), but it is also limited.
More information: https://en.wikipedia.org/wiki/IEEE_floating_point#IEEE_754-2008 and What is the maximum number in the mantissa part of a Java float?
If you need to have an unlimited precision, try BigDecimal. The count of significant digits there is only limited by the amount of your memory.
If you only need integer values, BigInteger is an option.
Study What Every Computer Scientist Should Know About Floating-Point Arithmetic, David Goldberg, 1991.
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Getting the first occurrence in binary search

I am trying to get the first occurrence of the number 5.The answer should be 2 in this case but i am getting 3 here.
public static void main(String[] args) {
int A[] = {1, 3, 5, 5, 5, 17, 20};
int index = BinarySearch(A, 5);
System.out.println(index);
}
public static int BinarySearch(int[] A, int x) {
int low = 0;
int high = A.length - 1;
while (low <= high) {
//(low + high) / 2
int mid = low + (high-low)/2; // more optimal -> low + (high-low)/2 (avoid integer overflow)
if (x == A[mid]) {
return mid;
} else if (x < A[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
When you find the value you are looking for, you return the mid index immediately without checking if there are smaller indices having the same value.
You have to continue searching :
public static int BinarySearch(int[] A, int x) {
int low = 0;
int high = A.length - 1;
int mid = -1;
while (low <= high) {
mid = low + (high-low)/2;
if (x <= A[mid]) { // this ensures you keep searching for the first index having
// the number you are looking for
//
// changing x <= A[mid] to x < A[mid] will give you the
// last index having the number you are looking for instead
high = mid - 1;
} else {
low = mid + 1;
}
}
if (mid >= 0 && x == A[mid]) {
return mid;
}
return -1;
}

Categories

Resources