Algorithm to find the largest square number smaller than n - java

How can I find the largest square number (ie 4, 9, 16) smaller than a given int n efficiently? I have the following attempt:
int square = (int)Math.sqrt(number);
return square*square;
But it has the obvious inefficiency of getting a square root just so we can square it.

Up front: It should be noted that processors capable of doing sqrt as a machine instruction will be fast enough. No doubt, its (micro)program uses Newton-Raphson, and this algorithm is of quadratic convergence, doubling the number of accurate digits with each iteration.
So, ideas like this one aren't really worth pursuing, although they use nice properties of squares, etc. (See the next proposal)
// compute the root of the biggests square that is a power of two < n
public static int pcomp( int n ){
long p2 = 1;
int i = 0;
while( p2 < n ){
p2 <<= 2;
i += 2;
}
p2 >>= 2;
i -= 2;
return (int)(p2 >>= i/2);
}
public static int squareLowerThan( int n ){
int p = pcomp(n);
int p2 = p*p; // biggest power of two that is a square < n
int d = 1; // increase using odd numbers until n is exceeded
while( p2 + 2*p + d < n ){
p2 += 2*p + d;
d += 2;
}
return p2;
}
But I'm sure that Newton's algorithm is faster. Quadratic convergence, remember.
public static int sqrt( int n ){
int x = n;
while( true ){
int y = (x + n/x)/2;
if( y >= x ) return x;
x = y;
}
}
This returns the integer square root. return x*x to get the square below n.

A linear-time algorithm:
int largestSquare(int n) {
int i = 0;
while ((i+1)*(i+1) < n) {
++i;
}
return i*i;
}

There is a newton algorithm to find square root, what you need is m^2 instead of m, in the given link
https://math.stackexchange.com/questions/34235/algorithm-for-computing-square-root-of-a-perfect-square-integer
Even if you want to find square directly instead of finding m, I don't think it will be faster than this.
And working code here
public static int squareLessThanN(int N)
{
int x=N;
int y=(x+N/x)/2;
while(y<x)
{
x=y;
y=(x+N/x)/2;
}
return x*x;
}
But it seems inbuilt square root seems to be faster anyway. Just measured the runtime for both.
class Square{
public static void main(String[] args)
{
long startTime = System.currentTimeMillis();
System.out.println(squareLessThanN(149899437943L));
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("Running time is "+totalTime);
startTime = System.currentTimeMillis();
System.out.println(normal(149899437943L));
endTime = System.currentTimeMillis();
totalTime = endTime - startTime;
System.out.println("Running time is "+totalTime);
}
public static long squareLessThanN(long N)
{
long x=N;
long y=(x+N/x)/2;
while(y<x)
{
x=y;
y=(x+N/x)/2;
}
return x*x;
}
public static long normal(long N)
{
long square = (long)Math.sqrt(N);
return square*square;
}
}
And the output is
149899060224
Running time is 1
149899060224
Running time is 0

Related

What causes Java version Random walk doesn't converge to expected value?

Basically, I implemented a Random walk program in Java. And I know the distance should converge to l * (n ^ 0.5)(l is the step length, n is the total steps). If let l equals to 1, then d = n ^ 0.5 or in other words: d = sqrt(n).
But, strangly, although I cannot find any error in my code, it just converges to unexpected value. For example, given l = 1, n = 100, d should converge to 10, but actually it converges to 8.86 after 1000000 times experiments.
Here is my code:
public class RandomWalk {
private int x = 0;
private int y = 0;
private final Random random = new Random();
private void move(int dx, int dy) {
x += dx;
y += dy;
}
private void randomWalk(int m) {
for (int i = 0; i < m; i++)
randomMove();
}
private void randomMove() {
boolean xOry = random.nextBoolean();
boolean plusOrminus = random.nextBoolean();
int delta = plusOrminus ? 1 : -1;
int dx = xOry ? delta : 0, dy = xOry ? 0 : delta;
move(dx, dy);
}
public double distance() {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}
public static double randomWalkMulti(int m, int n) {
double totalDistance = 0;
for (int i = 0; i < n; i++){
RandomWalk walk = new RandomWalk();
walk.randomWalk(m);
totalDistance += walk.distance();
}
return totalDistance/n ;
}
}
I've thought some possiblities. First I think it may be caused by that the generated boolean by random has bias. Second I think it may be caused by float precision lost. But as this is just a very simple use case, I don't think these two situations are possible.
Could any one tell me why it doesn't work as expected?
I don't think it's true that the distance should average out to √n. According to https://math.stackexchange.com/questions/103142/expected-value-of-random-walk, the square of the distance should average out to n, but that's not the same thing (since the average of the square roots of a set of numbers is not the same as the square root of their average).

Divide and Conquer Closest Pair Algorithm

I'm trying to create an algorithm that returns the closest pair from randomly generated points. I have finished the algorithm, however the divide and conquer method of the algorithm is not much faster than the brute-force method. What can I do to optimize the code so that it returns at (n log n) time?
import java.util.*;
import java.lang.*;
import static java.lang.Math.min;
import static java.lang.StrictMath.abs;
public class closestPair {
private static Random randomGenerator; // for random numbers
public static class Point implements Comparable<Point> {
public long x, y;
// Constructor
public Point(long x, long y) {
this.x = x;
this.y = y;
}
public int compareTo(Point p) {
// compare this and p and there are three results: >0, ==0, or <0
if (this.x == p.x) {
if (this.y == p.y)
return 0;
else
return (this.y > p.y)? 1 : -1;
}
else
return (this.x > p.x)? 1 : -1;
}
public String toString() {
return " ("+Long.toString(this.x)+","+Long.toString(this.y)+")";
}
public double distance(Point p) {
long dx = (this.x - p.x);
long dy = (this.y - p.y);
return Math.sqrt(dx*dx + dy*dy);
}
}
public static Point[] plane;
public static Point[] T;
public static Point[] Y;
public static int N; // number of points in the plane
public static void main(String[] args) {
// Read in the Size of a maze
Scanner scan = new Scanner(System.in);
try {
System.out.println("How many points in your plane? ");
N = scan.nextInt();
}
catch(Exception ex){
ex.printStackTrace();
}
scan.close();
// Create plane of N points.
plane = new Point[N];
Y = new Point[N];
T = new Point[N];
randomGenerator = new Random();
for (int i = 0; i < N; ++i) {
long x = randomGenerator.nextInt(N<<6);
long y = randomGenerator.nextInt(N<<6);
plane[i] = new Point(x, y);
}
Arrays.sort(plane); // sort points according to compareTo.
for (int i = 1; i < N; ++i) // make all x's distinct.
if (plane[i-1].x >= plane[i].x) plane[i].x = plane[i-1].x + 1;
//for (int i = 1; i < N; i++)
// if (plane[i-1].y >= plane[i].y) plane[i].y = plane[i-1].y + 1;
//
//
System.out.println(N + " points are randomly created.");
System.out.println("The first two points are"+plane[0]+" and"+plane[1]);
System.out.println("The distance of the first two points is "+plane[0].distance(plane[1]));
long start = System.currentTimeMillis();
// Compute the minimal distance of any pair of points by exhaustive search.
double min1 = minDisSimple();
long end = System.currentTimeMillis();
System.out.println("The distance of the two closest points by minDisSimple is "+min1);
System.out.println("The running time for minDisSimple is "+(end-start)+" mms");
// Compute the minimal distance of any pair of points by divide-and-conquer
long start1 = System.currentTimeMillis();
double min2 = minDisDivideConquer(0, N-1);
long end1 = System.currentTimeMillis();
System.out.println("The distance of the two closest points by misDisDivideConquer is "+min2);
System.out.println("The running time for misDisDivideConquer is "+(end1-start1)+" mms");
}
static double minDisSimple() {
// A straightforward method for computing the distance
// of the two closest points in plane[0..N-1].
// to be completed
double midDis = Double.POSITIVE_INFINITY;
for (int i = 0; i < N - 1; i++) {
for (int j = i + 1; j < N; j++) {
if (plane[i].distance(plane[j]) < midDis){
midDis = plane[i].distance(plane[j]);
}
}
}
return midDis;
}
static void exchange(int i, int j) {
Point x = plane[i];
plane[i] = plane[j];
plane[j] = x;
}
static double minDisDivideConquer(int low, int high) {
// Initialize necessary values
double minIntermediate;
double minmin;
double minDis;
if (high == low+1) { // two points
if (plane[low].y > plane[high].y) exchange(low, high);
return plane[low].distance(plane[high]);
}
else if (high == low+2) { // three points
// sort these points by y-coordinate
if (plane[low].y > plane[high].y) exchange(low, high);
if (plane[low].y > plane[low+1].y) exchange(low, low+1);
else if (plane[low+1].y > plane[high].y) exchange(low+1, high);
// compute pairwise distances
double d1 = plane[low].distance(plane[high]);
double d2 = plane[low].distance(plane[low+1]);
double d3 = plane[low+1].distance(plane[high]);
return ((d1 < d2)? ((d1 < d3)? d1 : d3) : (d2 < d3)? d2 : d3); // return min(d1, d2, d3)
} else { // 4 or more points: Divide and conquer
int mid = (high + low)/2;
double lowerPartMin = minDisDivideConquer(low,mid);
double upperPartMin = minDisDivideConquer(mid+1,high);
minIntermediate = min(lowerPartMin, upperPartMin);
int k = 0;
double x0 = plane[mid].x;
for(int i = 1; i < N; i++){
if(abs(plane[i].x-x0) <= minIntermediate){
k++;
T[k] = plane[i];
}
}
minmin = 2 * minIntermediate;
for (int i = 1; i < k-1; i++){
for(int j = i + 1; j < min(i+7,k);j++){
double distance0 = abs(T[i].distance(T[j]));
if(distance0 < minmin){
minmin = distance0;
}
}
}
minDis = min(minmin, minIntermediate);
}
return minDis;
}
}
Use the following method with the change for minDisSimple. You can get more performance.
static double minDisSimple() {
// A straightforward method for computing the distance
// of the two closest points in plane[0..N-1].
// to be completed
double midDis = Double.POSITIVE_INFINITY;
double temp;
for (int i = 0; i < N - 1; i++) {
for (int j = i + 1; j < N; j++) {
temp = plane[i].distance(plane[j]);
if (temp < midDis) {
midDis = temp;
}
}
}
return midDis;
}
Performance wise for small amount of points simple method is good but larger amount of points Divide and Conquer is good. Try number of points with 10, 100, 1000, 10000, 100000, 1000000.
One critical aspect in the minDisDivideConquer() is that the loop that constructs the auxiliary array T iterates through all the N points. Since there are O(N) recursive calls in total, making this pass through all the N points every time leads to a complexity of O(N^2), equivalent to that of the simple algorithm.
The loop should actually only consider the points with indices between low and high. Furthermore, it could be split into two separate loops that start from mid (forward and backward), and break when the checked distance is already too large.
Another possible improvement for the minDisDivideConquer() method, in the "4 or more points" situation is to prevent looking into pairs that were already considered in the recursive calls.
If my understanding is correct, the array T contains those points that are close enough on x axis to the mid point, so that there is a chance that a pair of points in T generates a distance smaller than those from the individual half sets.
However, it is not necessary to look into points that are both before mid, or both after mid (since these pairs were already considered in the recursive calls).
Thus, a possible optimization is to construct two lists T_left and T_right (instead of T) and check distances between pairs of points such that one is on the left of mid, and the other to the right.
This way, instead of computing |T| * (|T| - 1) / 2 distances, we would only look into |T_left| * |T_right| pairs, with |T_left| + |T_right| = |T|. This value is at most (|T| / 2) * (|T| / 2) = |T| ^ 2 / 4, i.e. around 2 times fewer distances than before (this is in the worst case, but the actual number of pairs can also be much smaller, inclusively zero).

Java IF Statement (Algorithm efficiency improvement)

I used method a that counts the amount of possible choices for getting like number 10 with numbers that are from 0 to 6. Problem is that it just takes too much time when x is like 50 or something. I just need some tips what I should do to make this faster.
Code
public static int count(int x) {
if (x < 0) {
return 0;
}
if (x == 0) {
return 1;
}
int result = 0;
for (int i = 1; i <= 6; i++) {
result += count(x - i);
}
return result;
}
This is a variation on Fibonacci except it is the sum of the last six values instead.
You can use a plain loop which will be faster than memorisation (the first time)
public static long count(int x) {
long a=0, b=0, c=0, d=0, e=0, f=1;
while(x-- > 0) {
long sum = a + b + c + d + e + f;
a = b; b = c; c = d; d = e; e = f;
f = sum;
}
return f;
}
If you call this repeatedly you may as well store all the values in the int range which is likely to be less than 30 the first time and retrieve these values after that.

Why does binary searching take REALLY long sometimes?

I wrote two programs that create random int[] and then generate random ints to search for. Well, really I only wrote one program, then modified the search method (like I'm supposed to for OOP, right?). I'm using System.nanoTime() to calculate the time lapsed during search. I get values of 10,000-13,000 consistently for the linear search, and MOSTLY times less than 5,000 for binary search(averaging in the sort time across all the searches, less than 4,000 typically for the search alone). Occasionally, though, it will through back times over 13,000, which is even longer than all but the very longest linear searches.
I have java priority set to Real Time in the task manager (Windows). What would cause the search time to triple or more? FWIW, I get about 1 spike in linear for every 3 or 4 in binary, and the linear spike is roughly twice the normal time (22,000 was the worst so far). Is it something with the way I've written the code?
If you don't want to read through the whole thing, in both cases I record the time in the line before calling the search method and the line after, so you can skip to the search method if you think that might be the problem. Reporting of that number is copy/pasted from one program to the other, but binary search program has more reporting that I thought would be interesting. Thanks in advance.
import java.util.Random;
public class LinearSearchTimer{
public static void main(String[] args){
int[] numbers;
final int MAX_RUNS;
double time1, time2, timeSpent, timeTotal;
int searchedNumber;
Random rand = new Random();
boolean result;
boolean[] resultArr;
int resultCounter;
double pctTrue;
MAX_RUNS = 1000;
numbers = new int[MAX_RUNS];
resultArr = new boolean[MAX_RUNS];
resultCounter = 0;
timeTotal = 0;
//build array with random ints
for(int k = 0; k < numbers.length; k++){
numbers[k] = rand.nextInt(10001);
}
//generate random number and search array for it, then store to result array
for(int i = 0; i < numbers.length; i++){
searchedNumber = rand.nextInt(10001);
time1 = System.nanoTime();
result = search(numbers, searchedNumber);
time2 = System.nanoTime();
resultArr[resultCounter] = result;
resultCounter++;
timeSpent = time2 - time1;
timeTotal += timeSpent;
}
pctTrue = resultPercent(resultArr);
System.out.println("Avg time : " + timeTotal / MAX_RUNS);
System.out.println("Percent found: " + pctTrue * 100);
}
//search algorithm, returns true if found, false if not
public static boolean search(int[] numbers, int searchedNumber){
int i = 0;
boolean found = false;
while (found == false && i < numbers.length){
if (numbers[i] == searchedNumber)
found = true;
i++;
}
return found;
}
public static double resultPercent(boolean[] arr){
int trueCount = 0;
double result;
double runs;
for(boolean element : arr){
if (element == true)
trueCount++;
}
runs = arr.length;//for calculating percentage as a forced double
result = trueCount / runs;
return result;
}
}
and then...
import java.util.Arrays;
import java.util.Random;
public class BinarySearchTimer{
public static void main(String[] args){
//declare variables
int[] numbers;
final int MAX_RUNS;
double time1, time2, timeSpent, timeTotal;
int searchedNumber;
Random rand;
boolean result;
boolean[] resultArr;
int resultCounter;
double pctTrue;
double timeSort1, timeSort2, timeSortSpent;
//instantiate variables
MAX_RUNS = 1000;
numbers = new int[MAX_RUNS];
resultArr = new boolean[MAX_RUNS];
resultCounter = 0;
timeTotal = 0;
rand = new Random();
//build array with random ints
for(int k = 0; k < numbers.length; k++){
numbers[k] = rand.nextInt(10001);
}
//sort array
timeSort1 = System.nanoTime();
Arrays.sort(numbers);
timeSort2 = System.nanoTime();
timeSortSpent = timeSort2 - timeSort1;
//generate random number and call search method
for(int i = 0; i < numbers.length; i++){
searchedNumber = rand.nextInt(10001);
//get start time
time1 = System.nanoTime();
result = search(numbers, searchedNumber, 0, numbers.length - 1);
//get end time
time2 = System.nanoTime();
resultArr[resultCounter] = result;
resultCounter++;
timeSpent = time2 - time1;
timeTotal += timeSpent;
}
pctTrue = resultPercent(resultArr);
System.out.println("Avg time : " + timeTotal / MAX_RUNS);
System.out.println("Percent found: " + pctTrue * 100);
System.out.println("Actual sort time: " + timeSortSpent);
System.out.println("Sort time shared per search: " + timeSortSpent / MAX_RUNS);
System.out.println("Effective average search time: " + (timeTotal / MAX_RUNS + timeSortSpent / MAX_RUNS));
}
//search algorithm, returns true if found, false if not
public static boolean search(int[] numbers, int searchedNumber, int low, int high){
int mid = (high + low) / 2;
if(low > high)
return false;
if(numbers[mid] == searchedNumber){
return true;
}
else if(searchedNumber < numbers[mid]){
mid--;
return search(numbers, searchedNumber, low, mid);
}
else if(searchedNumber > numbers[mid]){
mid++;
return search(numbers, searchedNumber, mid, high);
}
return false;
}
public static double resultPercent(boolean[] arr){
int trueCount = 0;
double result;
double runs;
for(boolean element : arr){
if (element == true)
trueCount++;
}
runs = arr.length;//for calculating percentage as a forced double
result = trueCount / runs;
return result;
}
}

Java Compute Average Execution Time

I want to compute the average execution time of x number of runs (i.e. 10)... I can easily execute 10 times using the loop in the main method, but how can I store the execution times & compute the average? I'm thinking this is something really simple, but I'm drawing a blank at the moment... Thanks in advanced!
import java.util.Arrays;
import java.util.Random;
public class OptQSort1 {
static boolean insertionSortCalled = false;
private static final Random random = new Random();
private static final int RANDOM_INT_RANGE = 9999;
private static int[] randomArray(int size) {
// Randomize data (array)
final int[] arr = new int[size];
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(RANDOM_INT_RANGE);
}
return arr;
}
// Sort
private static void sort(int[] arr) {
if (arr.length > 0)
sortInPlace(arr, 0, arr.length - 1);
}
private static void sortInPlace(int[] arr, int left, int right) {
// OptQSort1:
int size = right - left + 1;
if (size < 10 && !insertionSortCalled) {
insertionSortCalled = true;
insertionSort(arr, 0, arr.length - 1);
}
if (left >= right)
return; // sorted
final int range = right - left + 1;
int pivot = random.nextInt(range) + left;
int newPivot = partition(arr, left, right, pivot);
sortInPlace(arr, left, newPivot - 1);
sortInPlace(arr, newPivot + 1, right);
}
private static int partition(int[] arr, int left, int right, int pivot) {
int pivotVal = arr[pivot];
swapArrayVals(arr, pivot, right);
int storeIndex = left;
for (int i = left; i <= (right - 1); i++) {
if (arr[i] < pivotVal) {
swapArrayVals(arr, i, storeIndex);
storeIndex++;
}
}
swapArrayVals(arr, storeIndex, right);
return storeIndex;
}
private static void swapArrayVals(int[] arr, int from, int to) {
int fromVal = arr[from];
int toVal = arr[to];
arr[from] = toVal;
arr[to] = fromVal;
}
public static void insertionSort(int[] arr, int left, int right) {
int in, out;
for (out = left + 1; out <= right; out++) {
int temp = arr[out];
in = out;
while (in > left && arr[in - 1] >= temp) {
arr[in] = arr[in - 1];
--in;
}
arr[in] = temp;
}
}
public static void main(String[] args) {
long StartTime = System.nanoTime();
int runCount = 0;
// Array size
int[] arr = randomArray(1000);
int[] copy = Arrays.copyOf(arr, arr.length);
// Print original data (array)
System.out.println("The starting/unsorted array: \n"
+ Arrays.toString(arr));
sort(arr);
do {
// check the result
Arrays.sort(copy);
if (Arrays.equals(arr, copy)) {
System.out.println("The ending/sorted array: \n"
+ Arrays.toString(arr));
// print time
long TotalTime = System.nanoTime() - StartTime;
System.out.println("Total elapsed time (milliseconds) " + "is: "
+ TotalTime + "\n");
runCount++;
}
} while (runCount < 10);
}
}
You can compute the average by just measuring the total time for 10 iterations of your code, then divide that by 10.
e.g:
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10; ++i) {
doSort();
}
long elapsed = System.currentTimeMillis() - start;
long average = elapsed / 10;
}
As a helpful tip, use a named constant rather than a literal value for the number of iterations:
private final static int ITERATIONS = 10;
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; ++i) {
doSort();
}
long elapsed = System.currentTimeMillis() - start;
long average = elapsed / ITERATIONS;
}
This means you only have to change the number in one place if you want to run, say, 50 or 100 iterations.
You should also be aware that it is very difficult to get accurate timing results from this kind of experiment. It's a good idea to include a "warm-up" phase to allow the JIT to evaluate and optimize the code, and to have a much larger number of iterations:
private static final int WARMUP_ITERATIONS = 10000;
private static final int RUN_ITERATIONS = 100000;
public static void main(String[] args) {
// Warmup with no timing
for (int i = 0; i < WARMUP_ITERATIONS; ++i) {
doSort();
}
// Now the real test
long start = System.currentTimeMillis();
for (int i = 0; i < RUN_ITERATIONS; ++i) {
doSort();
}
long elapsed = System.currentTimeMillis() - start;
long average = elapsed / RUN_ITERATIONS;
}
To calculate the average time, you need the sum of the times. The sum of the times is the total time, so you don't even need to know the individual times or record them. Just take the end-to-end time and divide by the count.
int count = ...
long start = System.nanoTime();
for(int i=0;i<count;i++) {
// do something
}
long time = System.nanoTime() - start;
long averageTime = time/count;
The JIT doesn't fully warmup until you have done at least 10,000 iterations, so you might ignore the first 11,000 if this is practical.
A simple way to do this is
int count = ...
long start = 0;
for(int i=-11000;i<count;i++) {
if(i == 0) start = System.nanoTime();
// do something
}
long time = System.nanoTime() - start;
long averageTime = time/count;
BTW: Only include in the test time the things you want to time. Generating random numbers , for example, could take longer than the sort itself which could give misleading results.
EDIT: The compile threshold which determines when a method or loop is compiled is controlled with -XX:CompileThresholed= which defaults to 10000 on the server JVM and 1500 on the client JVM. http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
-XX:CompileThreshold=10000 Number of method invocations/branches before
compiling [-client: 1,500]
You can use a list of integers to store the result for each run, but you don't need it to calculate average, just divide totaltime by number of runs.
Btw, your measurements are not very good:
1) Generation of random arrays is included there
2) 10 runs is not enought
before the execution:
long start = System.currentTimeMillis();
after the execution:
long end = System.currentTimeMillis();
add each time in an ArrayList like this:
times.add(end-start);
get the average time:
Long total = 0;
for(Long l : times)
total += l;
System.out.println("Average Time: "+(total/times.size()));
Be careful the unit of time of the return value is a millisecond.
Just keep a second long value that is a running total of the Totaltime values. When the loop exits, just divide by runCount.
Alternatively, create an ArrayList<Long> to store the times. Each time you do one run, add Totaltime to the array. After the loop exits, you can average the values and also compute other statistics (min/max, standard deviation, etc.).

Categories

Resources