Decrease run time of this java program - java

I have made a program to input an array, and find the product of 3 largest numbers of the array such that:
The array constitutes of sub-arrays consisting of index increased one at a time.
That is, for an array of 10 elements, find the product considering the first 3 elements, then first 4 elements, then first 5 elements and so on.
Here's my code:
import java.io.*;
import java.util.Arrays;
public class monkmulti {
public static void main(String args[] ) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine()); // no. of elements
String xs = br.readLine(); //accepting those elements in a string
xs=xs+" ";
if(n<1||n>100000) //constraint
System.exit(0);
int i,temp,count=0;
for(i=0;i<xs.length();i++)
{
if(xs.charAt(i)==' ')
{
count++;
}
}
if(count!=n) //checks if no. of elements are equal to n
System.exit(0);
int[] x=new int[count];
int k=0;
temp=0;
for(i=0;i<xs.length();i++)
{
if(xs.charAt(i)==' ')
{
x[k++]=Integer.parseInt(xs.substring(temp, i));
temp=i+1;
}
}
count=0;
int len=x.length,j;
int[] x1=new int[len];
System.arraycopy(x, 0, x1, 0, len);
for(i=0;i<len;i++)
{
if(x[i]<1||x[i]>100000) //constraint for each element
System.exit(0);
}
int m1=0,m2=0,m3=0;
int max1=x[0],max2=x[0],max3=x[0];
/*code to be improved below from here*/
for(i=2;i<len;i++)
{
for(j=0;j<=i;j++)
{
for(k=0;k<i;k++)
{
if(x1[k]>x1[k+1])
{
temp=x1[k];
x1[k]=x1[k+1];
x1[k+1]=temp;
}
}
}
System.out.println(x1[i]*x1[i-1]*x1[i-2]);
}
}
}
Input:
n=user inputs no. of elements in array
xs= string accepting n elements separated by a space
Output:
product considering first 3 elements
product considering first 4 elements
so on..
Example:
Input:
5
6 2 9 3 8
Output:
-1
-1
108
162
432
Explanation
There are 5 integers 6,2,9,3,8
For the third index, the top 3 numbers are 6,2 and 9 whose product is 108.
For the fourth index, the top 3 numbers are 6,9 and 3 whose product is 162.
For the fifth index, the top 3 numbers are 6,9 and 8 whose product is 432.

If I understand, You need something like this:
The order is O(items * log(subgroups)) -> O(items * log(3)) -> O(items)
public static void product(int[] items, int subGroup) {
if (subGroup > 0 && items.length >= subGroup) {
// this save the largest numbers:
PriorityQueue<Integer> maxElements = new PriorityQueue<>(subGroup);
int product = 1;
for (int i = 0; i < items.length; i++) {
// the queue of largest numbers is full:
if (maxElements.size() == subGroup) {
// the minimum of previous largest number is lower than the new number
if (maxElements.peek() < items[i]) {
// swapping
product /= maxElements.poll();
product *= items[i];
maxElements.add(items[i]);
}
System.out.println(product);
} else {
product *= items[i];
maxElements.add(items[i]);
if (maxElements.size() < subGroup) {
//the queue of largest numbers isn't full
System.out.println("-1");
} else {
// the queue of largest numbers is full now
System.out.println(product);
}
}
}
}
}
public static void main(String[] args) {
int[] input = {6, 2, 9, 3, 8};
product(input, 3);
}
Output:
-1
-1
108
162
432
EDIT:
A little bit faster:
private static void heap(int[] maxElements) {
int candidate = 1;
if (maxElements[1] > maxElements[2]) {
candidate = 2;
}
if (maxElements[0] > maxElements[candidate]) {
int temp = maxElements[0];
maxElements[0] = maxElements[candidate];
maxElements[candidate] = temp;
}
}
private static int addElement(int k, int[] maxElements, int item) {
if (k < 3) {
maxElements[k++] = item;
if (k < 3) {
System.out.println("-1");
} else {
heap(maxElements);
System.out.println(maxElements[0] * maxElements[1] * maxElements[2]);
}
} else {
if (maxElements[0] < item) {
maxElements[0] = item;
heap(maxElements);
}
System.out.println(maxElements[0] * maxElements[1] * maxElements[2]);
}
return k;
}
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine()); // no. of elements
String line = br.readLine(); //accepting those elements in a string
int[] maxElements = new int[3];
int k = 0;
int item = 0;
char c;
for (int i = 0; i < line.length(); i++) {
c = line.charAt(i);
if (c == ' ') {
k = addElement(k, maxElements, item);
item = 0;
} else {
item = item * 10 + (c - '0');
}
}
addElement(k, maxElements, item);
}

The following solution removed all "number of elements" check code as it's not really necessary for the solution.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeSet;
public class MonkMulti
{
public static void main(String args[]) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Input a list of numbers separated by space");
String xs = br.readLine(); // accepting those elements in a string
// split input values by space
String[] numbers = xs.split("\\ ");
// create int array
int[] x = new int[numbers.length];
// convert all strings to int
for (int i = 0; i < numbers.length; i++)
{
x[i] = Integer.valueOf(numbers[i]);
}
for (int arraySize = 3; arraySize <= x.length; arraySize++)
{
calculateMonkMulti(x, arraySize);
}
}
/**
* Calculate the product for different sizes
*
* #param values
* #param arraySize
*/
private static void calculateMonkMulti(int[] values, int arraySize)
{
// new target sub array
int[] sorted = new int[arraySize];
// copy values of the sub array
System.arraycopy(values, 0, sorted, 0, arraySize);
// sort the sub array
Arrays.sort(sorted);
// calculate start index of top 3 elements
int startIndex = sorted.length - 3;
// get top three elements
System.err.println(startIndex + "/" + arraySize + "/" + sorted.length);
int int1 = sorted[startIndex];
int int2 = sorted[++startIndex];
int int3 = sorted[++startIndex];
// calculate
long result = int1 * int2 * int3;
// output (or return)
System.out.println(int1 + " * " + int2 + " * " + int3 + " = " + result);
}
}
Solution using JDK classes and functions.
I get the following runtime values:
For the total array with 100.000 values from the link: Runtime: 47 ms for all sub arrays 100000 elements
For a runtime over all sub array of x elements, starting the following (updated after finished):
Total runtime: 66 ms after 1000 elements with 100000 elements total
Total runtime: 195 ms after 2000 elements with 100000 elements total
Total runtime: 317 ms after 3000 elements with 100000 elements total
...
Total runtime: 295667 ms after 98000 elements with 100000 elements total
Total runtime: 301906 ms after 99000 elements with 100000 elements total
Total runtime: 308266 ms after 100000 elements with 100000 elements total
Runtime: 308266 ms for all sub arrays 100000 elements
I do not know what the web site measures. The compilation time?
Version 2:
A new optimized version can solve the problem in total Runtime: 26900 ms for all sub arrays 100000 elements
And now Version 3:
...
Total runtime: 54 ms after 98000 elements with 100000 elements total
Total runtime: 54 ms after 99000 elements with 100000 elements total
Total runtime: 54 ms after 100000 elements with 100000 elements total
Runtime: 55 ms for all sub arrays 100000 elements
My fastest solution:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
public class MonkMulti
{
static int[] sortedFast;
public static void main(String args[]) throws IOException
{
BufferedReader br = new BufferedReader(new FileReader(new File(
"u:/temp/numbers.txt")));
String xs = br.readLine(); // accepting those elements in a string
// split input values by space
long startTime = System.currentTimeMillis();
String[] numbers = xs.split("\\ ");
// create int array
int[] sourceValues = new int[numbers.length];
// convert all strings to int
for (int i = 0; i < numbers.length; i++)
{
sourceValues[i] = Integer.valueOf(numbers[i]);
}
// create memory array for top 3 values
sortedFast = new int[3];
// copy first to elements from source to memory by one position up
System.arraycopy(sourceValues, 0, sortedFast, 1, 2);
// and set the lowest element to -1
sortedFast[0] = -1;
long result = 0;
// start with 3 elements in sub array
int startIndex = 3;
// loop all sub arrays from 3 .. sourceValues.leength
for (int arraySize = startIndex; arraySize <= sourceValues.length; arraySize++)
{
// calculate the value
result += calculateMonkMegaFast(sourceValues, arraySize);
}
long endTime = System.currentTimeMillis();
System.out
.println("Runtime: " + (endTime - startTime)
+ " ms for all sub arrays " + sourceValues.length
+ " elements (some value to ignore to prevent optimization: " + result + ")");
}
private final static long calculateMonkMegaFast(int[] values, int arraySize)
{
// check if the next element if greater as the lowest element in memory
// sort the sub array
if (values[arraySize - 1] > sortedFast[0])
{
// yes, replace lowest element with the bigger element from source
sortedFast[0] = values[arraySize - 1];
// sort the 3 element memory array
Arrays.sort(sortedFast);
}
// get new top 3 elements
int int1 = sortedFast[0];
int int2 = sortedFast[1];
int int3 = sortedFast[2];
// calculate result
long result = int1 * int2 * int3;
// output (or return)
// System.out.println(int1 + " * " + int2 + " * " + int3 + " = " +
// result);
return result;
}
}
Result calculation should use double to prevent value overflow. The complexity is almost O(n).

Related

How do I implement a binary search for finding a start element, a middle element and end element? Java

I am creating a program which allows the user to enter 3 numbers which will be found in a sorted array and the index will be returned using binary search.
I have done the sorting of the array and the binary search for one user input. How do I implement this for finding three numbers? a start num, a middle num and end num.
I have provided code, input and output.
Code:
import java.util.Scanner;
import java.util.Random;
import java.util.Arrays;
public class App {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("\n" + " ");
// generating n, 0 < n < 10000 and 0 < length < 100
Random rand = new Random();
int[] arr = new int[100];
for (int z = 0; z < arr.length; z++) {
arr[z] = rand.nextInt(10000);
}
// sorting
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// binary search
//pointers
int begin = 0;
int last = arr.length - 1;
int start = 0;
int x = scanner.nextInt();
//loop
while (begin <= last) {
start = (begin + last) / 2;
if (arr[start] < x) {
begin = start + 1;
}
else if (arr[start] > x) {
last = start - 1;
}
else {
break;
}
}
System.out.println("Element found at " + start);
}
}
I want to keep it as simple as possible. Thanks.
You should extract the binarySearch functionality into a separate method and call this method in a loop.
It is also possible to print if the exact element is found in the array, or if insertion point is detected.
static int binarySearch(int[] arr, int begin, int last, int x) {
int start = -1;
boolean found = false;
while (begin <= last) {
start = (begin + last) / 2;
if (arr[start] < x) {
begin = start + 1;
}
else if (arr[start] > x) {
last = start - 1;
}
else {
found = true;
break;
}
}
System.out.printf("Element %d %s at index=%d%n", x, found ? "found" : "can be inserted at", start);
return start;
}
Test loop: the array of user inputs is sorted forcefully, start is updated for each the following iteration:
//....
System.out.println("Input 3 numbers: ");
int[] inp = new int[3];
for (int i = 0; i < inp.length; i++) {
inp[i] = scanner.nextInt();
}
Arrays.sort(inp);
for (int i = 0, start = 0; i < inp.length; i++) {
start = binarySearch(arr, start, arr.length - 1, inp[i]);
}
Sample output:
[714, 726, 1016, 1108, 1124, 1221, 1291, 1316, 1394, 1412, 1455, 1557, 1604, 1674, 1737,
1893, 1961, 2105, 2243, 2258, 2266, 2318, 2337, 2545, 2608, 2740, 3029, 3066, 3077, 3097,
3155, 3159, 3257, 3262, 3587, 3602, 3609, 3745, 4155, 4380, 4497, 4517, 4529, 4576, 4902,
4943, 5029, 5103, 5302, 5364, 5504, 5572, 5750, 5820, 5902, 6033, 6043, 6222, 6240, 6271,
6357, 6358, 6359, 6369, 6384, 6447, 6598, 6657, 6687, 6720, 6834, 6905, 7082, 7106, 7133,
7144, 7426, 7436, 7451, 8232, 8286, 8320, 8402, 8444, 8511, 8591, 8680, 8801, 8895, 8994,
9074, 9133, 9162, 9315, 9403, 9607, 9691, 9691, 9893, 9957]
Input 3 numbers:
15 726 9607
Element 15 can be inserted at at index=0
Element 726 found at index=1
Element 9607 found at index=95
A stream based solution is more concise:
Use stream of random integers provided with Random::ints
Use IntStream.generate to get user inputs via method reference Scanner::nextInt
Sorting streams with sorted method
Need to use a 1-element array to store the intermediate value of start and pass it to binarySearch method.
int[] arr = new Random().ints(0, 10000).limit(100).sorted().toArray();
System.out.println(Arrays.toString(arr));
Scanner scanner = new Scanner(System.in);
System.out.println("Input 3 int numbers: ");
int[] start = new int[1];
IntStream.generate(scanner::nextInt)
.limit(3)
.sorted()
.mapToObj(x -> Arrays.asList(x, start[0] = binarySearch(arr, start[0], arr.length - 1, x)))
.forEach(res -> System.out.printf("index of %d is %d%n", res.get(0), res.get(1)));

Why does the compiler give an " java.lang.ArrayIndexOutOfBoundsException: x -error? (size depends on input)

I am making a small program to guess the keylength of a vigenere-cipher, with a given length between 5-15, within java.
I do this by calculating standard deviations because of the transpositions.
But my real question is why I get an out of bound error in the array in the method "countFrequency". I can't seem to see where it goes out of bounds.
I know my program could be way more efficient, but I think it should work once this error is fixed.
My program code is below.
Thanks!
import java.awt.List;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class main {
// Scanning the encrypted text
// private static char encryptedText[];
public static void main(String[] args) throws IOException{
Scanner scan = null;new Scanner(System.in);
scan = new Scanner(System.in);
int i;
int vectors[][];
System.out.println("Give the decrypted text: \n");
String encryptedText = scan.next().toString();
vectors = makeArray();
vectors = countFrequency(vectors, encryptedText);
calculateDeviations(vectors);
// No need to scan more
if(scan!=null)
scan.close();
}
private static void calculateDeviations(int[][] vectors) {
int i;
int j;
int sumpowfreq;
int freqpowsum;
for(i=0;i<12;i++){
double deviation =0;
for(j=0; j<26;j++){
sumpowfreq = sumPowwFreq(vectors,i);
freqpowsum = freqPowwSum(vectors,i);
deviation = Math.sqrt((sumpowfreq/26) - (freqpowsum/26));
}
System.out.println("The devation of " + j + " is " + deviation + ".");
}
}
private static int freqPowwSum(int[][] vectors, int i) {
int powsum=0;
int sum=0;
int j;
for(j=0;j<26;j++){
sum = sum + vectors[j][i];
}
powsum = sum * sum;
return powsum;
}
public static int sumPowwFreq(int[][] vectors, int i) {
int sum=0;
int j;
for(j=0;j<26;j++){
sum = sum + (vectors[j][i] * vectors[j][i]);
}
return sum;
}
public static int[][] makeArray() {
// Making the 2-dimensional array and set it to 0
int keySize;
int letterFrequency[][] = new int[26][11];
for(keySize=5;keySize<16;keySize++){
int j;
for(j=0;j<26;j++){
letterFrequency[j][keySize-5] = 0;
}
}
return letterFrequency;
}
public static int[][] countFrequency(int freq[][], String encryptedText){
int i,j,c;
int splitSize;
int ascii;
String splittedText[];
for(splitSize=5; splitSize<15; splitSize++){
splittedText = splitText(splitSize, encryptedText);
for(j=0;j<splitSize;j++){
for(c=0;c<splittedText[j].length();c++){
ascii= splittedText[j].charAt(c);
ascii = ascii - 97; // because the ascii table starts at 97, 0 represents an 'a' now
// and we assumed that the encrypted text only contained small letters
freq[ascii][j]++;
}
}
}
return freq;
}
public static String[] splitText(int partLength, String encryptedText){
int len = encryptedText.length();
int amountparts = len / (partLength);
String parts[] = new String[amountparts];
// Break into parts
int offset= 0;
int i = 0;
while (i < amountparts){
parts[i] = encryptedText.substring(offset, Math.min(offset + partLength, len));
offset += partLength;
i++;
}
return parts;
}
}
I am no crypto-freak but I went into your code and saw the exception.
I think, your problem rather domain-specific than technical...
The nested for loops in countFrequency() expects the array splittedText to contain at least 15 elements.
for 1) splitSize runs from 5 to 14
for 2) for every splitSize,j runs
from 0 to splitSize
for 3) for every j, do something with the jth element of splittedText
The last for is important for the Exception:
splittedText must contain at least 15 elements.
How many elements are in splittedText?
This is determined in splitText(int, String). This method is called within (for 1) with the current splitSize as partLength and the user-input as encryptedText.
splitText returns an array with (encryptedText.length() / partlength) elements. So we have:
splittedText.length() = (inputlength / splitSize)
For every splitSize (5 to 14), the input-length must hold following condition:
input-length/splitSize >= splitSize ; and this is equivalent to
input-length >= splitSize * splitSize
for the splitSize of 14, your input must be greater than 196.
As far as I read, this is no constraint for vigenere-ciphers. And - btw - if you pass this first ArrayOutOfBoundException, you will run into the next Exception.

corresponding permutation number

I have a given word, for wich I need to find its number of permutation on its corresponding sorted word .
Say I have word BABA , its corresponding sorted word would be, AABB, if I start permuting this sorted word, would come to AABB as a second "word", regardless of letter repetition, then ABAB, ABBA , BABA .. so the permute number for word BABA is 5 .
The easy way would be start doing all possible combinations, and then compared with the initial word .
so far , ive done..
import java.util.Arrays;
public class Permutation {
int location =1;
public static char[] warray;
void printArray(char []a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
System.out.println("location " + location );
}
void permute(char []a,int k ) {
if(k==a.length) {
location++;
// Check if the permuted word is the one looking for.
if (Arrays.equals(a, warray))
{ System.out.println("final iteration k" + k);
printArray(a);
System.exit(0);}
}
else
for (int i = k; i < a.length; i++) {
char temp=a[k];
a[k]=a[i];
a[i]=temp;
permute(a,k+1);
}
}
public static void main(String[] args) {
if (args[0].length() > 25 ) {
System.out.println(" Word not in permited range " );
System.exit(0);
}
else {
Permutation p=new Permutation();
warray = new char[args[0].length()];
char [] wpermute = new char[args[0].length()];
for (int i = 0; i < args[0].length(); i++) {
warray[i] = new Character(args[0].charAt(i));
wpermute[i] = new Character(args[0].charAt(i));
}
Arrays.sort(wpermute);
System.out.print("sorted word : " );
for (int i = 0; i < wpermute.length; i++) {
System.out.print(wpermute[i]);
}
p.permute(wpermute,0);
}
}
But this could be very slow performance.
My second guess, would be , starting like a binary search startting with first letter of unsorted word, calculate possibble permutations to have this letter as the first letter on permutations, and then second letter..and so ... would that sound good ?
If you only have 2 letters and if the length of the word is N and the number of A's is n then the number of permutations is N choose n.
If you have N letters total and n_a, n_b, ..., n_z describe the number of each letter then the total number of permutations is
N!/(n_a! n_b! n_c! ... n_z!)
Check out Multinomials, scroll down to the bit on permutations.
Another word would be QUESTION , its sorted word is EINOQSTU .
for question , q is in position 1 , and in postion 5 in the new word, how many permutations need to do to put is in postion 1 = 20161 .
Now I take second letter in question, is U, which is in position 8 in sorted word , how many permutations need to do, is 24481
I think, I could calculate , not perform, the number permutations needed to put a letter in y position, to be in X position. and then , the sum of all , would be the permutations needed for the whold word.
Now, how to calculate those numbers, I know has to be with factorial plus something else ..is not ?
So I finally completed the code, and yes, needed to check the multinomials.
also got part of the idea from a related post here.
But here my code.
package WordPuzzle;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/** Rafael G. */
public class WordPuzzle {
static String sortWord (String[] wordinput){
char [] wsorted = new char[wordinput[0].length()];
wsorted = wordinput[0].toCharArray();
Arrays.sort(wsorted);
String aux="";
for (int i = 0; i < wsorted.length; i++) {
aux = aux + wsorted[i];
}
return aux;
}
static void calculatePerm(String wordtofind,String wordsorted)
{
int sum = 0;
int numberpermutations;
char nextchar;
int charlocremainder =0;
String lowerLetters;
String greaterLetters;
Map<Character, Integer> characterCounts = new HashMap<Character, Integer> ();
int count ;
char letter;
int factorial;
int [] factorials = new int [wordsorted.length()+1];
factorial =1;
numberpermutations = 1;
int nMinusI;
int nextcharcount;
// set a mapping of repeated letters and its number
// and store factorial calculation.
for (int i = 0; i < wordsorted.length(); i++) {
letter = wordsorted.charAt(i);
factorial = factorial * (i+1);
factorials[i+1]= factorial;
count = characterCounts.containsKey(letter) ? characterCounts.get(letter) + 1 : 1;
characterCounts.put(letter, count);
}
String trimWord = new String(wordsorted);
for (int i = 0; i < wordtofind.length() ; i++){
nMinusI = wordtofind.length()-(i+1);
nextchar = wordtofind.charAt(i);
charlocremainder = trimWord.indexOf(nextchar);
lowerLetters = trimWord.substring(0, charlocremainder);
// Calculate the denominator which is the number of repeated letters
// of the formula (N-i)! * (Na+Nb) /Na!Nb!..
nextcharcount = characterCounts.get(nextchar);
characterCounts.put(nextchar, nextcharcount-1);
int denomfact = factorials[nextcharcount];
if (lowerLetters.length() > 1){
char x = lowerLetters.charAt(0);
char y = x;
for (int k = 1 ; k < lowerLetters.length(); k++){
y = lowerLetters.charAt(k);
if (x != y) {
denomfact = denomfact * factorials[characterCounts.get(x)];
x = y;
}
}
denomfact = denomfact * factorials[characterCounts.get(y)];
}
numberpermutations = factorials[nMinusI] * lowerLetters.length() / denomfact;
sum = sum + numberpermutations;
greaterLetters= trimWord.substring(charlocremainder+1);
trimWord = lowerLetters.concat(greaterLetters);
}
System.out.println(" Rank of permutation " + (sum+1));
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
long startTime = System.nanoTime();
String wordsorted;
String wordentered;
if (args[0].length() > 25 ) {
System.out.println("Word not in permited range " );
System.exit(0);
}
else {
wordentered = args[0].toUpperCase();
wordsorted = sortWord(args).toUpperCase();
calculatePerm(wordentered,wordsorted);
}
long endTime = System.nanoTime();
System.out.println("Took "+(endTime - startTime)/1000000000.0 + " seconds");
System.out.println("Took "+(endTime - startTime)* 0.000001 + " milliseconds");
}
}

TreeSet search taking long time ,puzzle: to find lucky numbers

It actually is problem to find lucky number - those numbers whose sum of digits and sum of square of digits are prime. I have implemented Sieve of Eratosthenes. Now to optimize it further I commented my getDigitSum method, that I suppose was heavy and replaced with two hard-coded value , but it is still taking minutes to solve one test case. Here is a reference to actual problem asked
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.TreeSet;
public class Solution {
private static int[] getDigitSum(long num) {
long sum = 0;
long squareSum = 0;
for (long tempNum = num; tempNum > 0; tempNum = tempNum / 10) {
if (tempNum < 0) {
sum = sum + tempNum;
squareSum = squareSum + (tempNum * tempNum);
} else {
long temp = tempNum % 10;
sum = sum + temp;
squareSum = squareSum + (temp * temp);
}
}
int[] twosums = new int[2];
twosums[0] = Integer.parseInt(sum+"");
twosums[1] = Integer.parseInt(squareSum+"");
// System.out.println("sum Of digits: " + twoDoubles[0]);
// System.out.println("squareSum Of digits: " + twoDoubles[1]);
return twosums;
}
public static Set<Integer> getPrimeSet(int maxValue) {
boolean[] primeArray = new boolean[maxValue + 1];
for (int i = 2; i < primeArray.length; i++) {
primeArray[i] = true;
}
Set<Integer> primeSet = new TreeSet<Integer>();
for (int i = 2; i < maxValue; i++) {
if (primeArray[i]) {
primeSet.add(i);
markMutiplesAsComposite(primeArray, i);
}
}
return primeSet;
}
public static void markMutiplesAsComposite(boolean[] primeArray, int value) {
for (int i = 2; i*value < primeArray.length; i++) {
primeArray[i * value] = false;
}
}
public static void main(String args[]) throws NumberFormatException,
IOException {
// getDigitSum(80001001000l);
//System.out.println(getPrimeSet(1600));
Set set = getPrimeSet(1600);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int totalCases = Integer.parseInt(br.readLine());
for (int cases = 0; cases < totalCases; cases++) {
String[] str = br.readLine().split(" ");
long startRange = Long.parseLong(str[0]);
long endRange = Long.parseLong(str[1]);
int luckyCount = 0;
for (long num = startRange; num <= endRange; num++) {
int[] longArray = getDigitSum(num); \\this method was commented for testing purpose and was replaced with any two hardcoded values
if(set.contains(longArray[0]) && set.contains(longArray[1])){
luckyCount++;
}
}
System.out.println(luckyCount);
}
}
}
what I should use to cache the result so that it takes lesser amount of time to search, currently it takes huge no. of minutes to complete 10000 test cases with range 1 99999999999999(18 times 9 -the worst case) , even thought the search values have been hard-coded for testing purpose( 1600, 1501 ).
You need a different algorithm. Caching is not your problem.
If the range is large - and you can bet some will be - even a loop doing almost nothing would take a very long time. The end of the range is constrained to be no more than 1018, if I understand correctly. Suppose the start of the range is half that. Then you'd iterate over 5*1017 numbers. Say you have a 2.5 GHz CPU, so you have 2.5*109 clock cycles per second. If each iteration took one cycle, that'd be 2*108 CPU-seconds. A year has about 3.1*107 seconds, so the loop would take roughly six and a half years.
Attack the problem from the other side. The sum of the squares of the digits can be at most 18*92, that's 1458, a rather small number. The sum of the digits itself can be at most 18*9 = 162.
For the primes less than 162, find out all possible decompositions as the sum of at most 18 digits (ignoring 0). Discard those decompositions for which the sum of the squares is not prime. Not too many combinations are left. Then find out how many numbers within the specified range you can construct using each of the possible decompositions (filling with zeros if required).
There are few places in this implementation that can be improved. In order to to start attacking the issues i made few changes first to get an idea of the main problems:
made the total start cases be the value 1 and set the range to be a billion (1,000,000,000) to have a large amount of iterations. also I use the method "getDigitSum" but commented out the code that actually makes the sum of digits to see how the rest runs: following are the methods that were modified for an initial test run:
private static int[] getDigitSum(long num) {
long sum = 0;
long squareSum = 0;
// for (long tempNum = num; tempNum > 0; tempNum = tempNum / 10) {
// if (tempNum < 0) {
// sum = sum + tempNum;
// squareSum = squareSum + (tempNum * tempNum);
// } else {
// long temp = tempNum % 10;
// sum = sum + temp;
// squareSum = squareSum + (temp * temp);
//
// }
// }
int[] twosums = new int[2];
twosums[0] = Integer.parseInt(sum+"");
twosums[1] = Integer.parseInt(squareSum+"");
// System.out.println("sum Of digits: " + twoDoubles[0]);
// System.out.println("squareSum Of digits: " + twoDoubles[1]);
return twosums;
}
and
public static void main(String args[]) throws NumberFormatException,
IOException {
// getDigitSum(80001001000l);
//System.out.println(getPrimeSet(1600));
Set set = getPrimeSet(1600);
//BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int totalCases = 1;
for (int cases = 0; cases < totalCases; cases++) {
//String[] str = br.readLine().split(" ");
long startRange = Long.parseLong("1");
long endRange = Long.parseLong("1000000000");
int luckyCount = 0;
for (long num = startRange; num <= endRange; num++) {
int[] longArray = getDigitSum(num); //this method was commented for testing purpose and was replaced with any two hardcoded values
if(set.contains(longArray[0]) && set.contains(longArray[1])){
luckyCount++;
}
}
System.out.println(luckyCount);
}
}
Running the code takes 5 minutes 8 seconds.
now we can start optimizing it step by step. I will now mention the various points in the implementation that can be optimized.
1- in the method getDigitSum(long num)
int[] twosums = new int[2];
twosums[0] = Integer.parseInt(sum+"");
twosums[1] = Integer.parseInt(squareSum+"");
the above is not good. on every call to this method, two String objects are created , e.g. (sum+"") , before they are parsed into an int. considering the method is called billion times in my test, that produces two billion String object creation operations. since you know that the value is an int (according to the math in there and based on the links you provided), it would be enough to use casting:
twosums[0] = (int)sum;
twosums[1] = (int)squareSum;
2- In the "Main" method, you have the following
for (long num = startRange; num <= endRange; num++) {
int[] longArray = getDigitSum(num); \\this method was commented for testing purpose and was replaced with any two hardcoded values
if(set.contains(longArray[0]) && set.contains(longArray[1])){
luckyCount++;
}
}
here there are few issues:
a- set.contains(longArray[0]) will create an Integer object (with autoboxing) because contains method requires an object. this is a big waste and is not necessary. in our example, billion Integer objects will be created. Also, usage of set, whether it is a treeset or hash set is not the best for our case.
what you are trying to do is to get a set that contains the prime numbers in the range 1 .. 1600. this way, to check if a number in the range is prime, you check if it is contained in the set. This is not good as there are billions of calls to the set contains method. instead, your boolean array that you made when filling the set can be used: to find if the number 1500 is prime, simply access the index 1500 in the array. this is much faster solution. since its only 1600 elements (1600 is greater than max sum of sqaures of digits of your worst case), the wasted memory for the false locations is not an issue compared to the gain in speed.
b- int[] longArray = getDigitSum(num);
an int array is being allocated and returned. that will happen billion times. in our case, we can define it once outside the loop and send it to the method where it gets filled. on billion iterations, this saved 7 seconds, not a big change by itslef. but if the test cases are repeated 1000 times as you plan, that is 7000 second.
therefore, after modifying the code to implement all of the above, here is what you will have:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Set;
import java.util.TreeSet;
public class Solution {
private static void getDigitSum(long num,int[] arr) {
long sum = 0;
long squareSum = 0;
// for (long tempNum = num; tempNum > 0; tempNum = tempNum / 10) {
// if (tempNum < 0) {
// sum = sum + tempNum;
// squareSum = squareSum + (tempNum * tempNum);
// } else {
// long temp = tempNum % 10;
// sum = sum + temp;
// squareSum = squareSum + (temp * temp);
//
// }
// }
arr[0] = (int)sum;
arr[1] = (int)squareSum;
// System.out.println("sum Of digits: " + twoDoubles[0]);
// System.out.println("squareSum Of digits: " + twoDoubles[1]);
}
public static boolean[] getPrimeSet(int maxValue) {
boolean[] primeArray = new boolean[maxValue + 1];
for (int i = 2; i < primeArray.length; i++) {
primeArray[i] = true;
}
for (int i = 2; i < maxValue; i++) {
if (primeArray[i]) {
markMutiplesAsComposite(primeArray, i);
}
}
return primeArray;
}
public static void markMutiplesAsComposite(boolean[] primeArray, int value) {
for (int i = 2; i*value < primeArray.length; i++) {
primeArray[i * value] = false;
}
}
public static void main(String args[]) throws NumberFormatException,
IOException {
// getDigitSum(80001001000l);
//System.out.println(getPrimeSet(1600));
boolean[] primeArray = getPrimeSet(1600);
//BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int totalCases = 1;
for (int cases = 0; cases < totalCases; cases++) {
//String[] str = br.readLine().split(" ");
long startRange = Long.parseLong("1");
long endRange = Long.parseLong("1000000000");
int luckyCount = 0;
int[] longArray=new int[2];
for (long num = startRange; num <= endRange; num++) {
getDigitSum(num,longArray); //this method was commented for testing purpose and was replaced with any two hardcoded values
if(primeArray[longArray[0]] && primeArray[longArray[1]]){
luckyCount++;
}
}
System.out.println(luckyCount);
}
}
}
Running the code takes 4 seconds.
the billion iterations cost 4 seconds instead of 5 minutes 8 seconds, that is an improvement. the only issue left is the actual calculation of the sum of digits and sum of squares of digits. that code i commented out (as you can see in the code i posted). if you uncomment it, the runtime will take 6-7 minutes. and here, there is nothing to improve except if you find some mathematical way to have incremental calculation based on previous results.

Permutation - Need help desining a better block that won't need unused values

I need help making my permutation's (order important, repeatable) size smaller by discarding the unneeded permutations before hand.
The current permutation takes a global minimum and maximum from the values provided. However, some of the permutations are discarded afterward as they don't fall within the range needed.
The idea is there are for example 3 numbers which need a permutation. For example: 1-3, 8-10 and 5-15. The current code will create a permutation of 1-15 even though values from 4-7 will be discarded later on.
Unfortunately in some instances its not possible to create an array large enough in Java to contain the permutation results.
Any help would be appreciated on changing this permutation to only include necessary values prior to computing the permutation.
PermutationCore:
public class PermutationCore {
private String[] a;
private int n;
public PermutationCore(String[] arrayOfPossibilities, int lengthOfPermutation) {
this.a = arrayOfPossibilities;
this.n = lengthOfPermutation;
}
public String[][] getVariations() {
int l = a.length;
int permutations = (int) Math.pow(l, n);
Co.println("Permutation array size: " + permutations);
String[][] table = new String[permutations][n];
for (int x = 0; x < n; x++) {
int t2 = (int) Math.pow(l, x);
for (int p1 = 0; p1 < permutations;) {
for (int al = 0; al < l; al++) {
for (int p2 = 0; p2 < t2; p2++) {
table[p1][x] = a[al];
p1++;
}
}
}
}
return table;
}
}
Permutation
public class Permutation {
private ArrayList<Iteration> listOfIteration = new ArrayList<Iteration>();
private boolean prepared;
private PermutationCore permutationCore;
private int min = Integer.MAX_VALUE;
private int max = Integer.MIN_VALUE;
private int count = 0;
private String[][] arrayOfStringResults;
public void addIteration(Iteration iteration){
if (prepared){throw new IllegalStateException("Permuation is already prepared. Create a new instance to add new items");}
this.listOfIteration.add(iteration);
}
public void prepare(){
String[] arrayOfString;
for (Iteration iteration : listOfIteration){
if (iteration.end > max){max = iteration.end;}
if (iteration.start < min){min = iteration.start;}
}
arrayOfString = new String[max-min+1];
for (int i=0; i<arrayOfString.length; i++){
arrayOfString[i] = String.valueOf(min+i);
}
permutationCore = new PermutationCore(arrayOfString, listOfIteration.size());
prepared = true;
// Co.println("Min/max: " + min + "," + max);
arrayOfStringResults = permutationCore.getVariations();
// ArrayTools.sort2DStringArray(arrayOfStringResults);
}
public boolean iterate(){
LABEL_ITERATE_LOOP: {
int i=0;
if (count == arrayOfStringResults.length){
return false;
}
for (Iteration iteration : listOfIteration){
int currentValue = Integer.valueOf(arrayOfStringResults[count][i]);
if (currentValue > iteration.end || currentValue < iteration.start){
//Co.println("Failed at: " + iteration.start + "," + iteration.end + " / " + currentValue);
count++;
break LABEL_ITERATE_LOOP;
}
iteration.current = currentValue;
i++;
}
count++;
}
return true;
}
public Iteration getIteration(Object request) {
for (Iteration iteration : listOfIteration){
if (iteration.request == request){
return iteration;
}
}
return null;
}
public ArrayList<Iteration> getListOfIterations(){
return listOfIteration;
}
public static class Iteration{
private int start;
private int end;
private int current;
private Object request;
public Iteration(int start, int end, Object request){
this.start = start;
this.end = end;
this.request = request;
}
public double getCurrentValue(){
return this.current;
}
public Object getRequest(){
return this.request;
}
}
}
This of your problem as permuting k numbers from 0 to (n-1), and printing a[n] instead of n. :) That is what you can do, to reduce iterations.
The other way to do it, is to use a number between 0 to n!-1 and figure out what the current permutation is, and print it. Although it is a slower method, it's faster to resume operations in this format - and we can quickly print the kth permutation.
Let us say the numbers are: 1, 2, 3, 4. There are a total of 4! permutation = 24, possible. To print the 15th (counting from zero) permutation, here's what we do:
n = 4
a = 1 2 3 4
divide 15 by (n-1)!
we get 15/6 = 2, reminder = 3.
So the permutation starts with a[2] = 3.
a = 1 2 4
take the reminder, divide by (n-2)!
we get 3/2 = 1, reminder = 1.
so the permutation is now permutation, a[1] = 3, 2
a = 1 4
take the reminder, divide by (n-1)!
we get 1/1 = 1, reminder = 0
so the permutation is now permutation, a[1] = 3, 2, 4.
do until reminder is zero. print a[0]= 3, 2, 4, 1.
^ this is the most efficient way to generate the kth permutation of any series.
You can use BigInteger math to perform this method very efficiently.

Categories

Resources