I need to iterate a HashMap containing integers for 10^5 turns. I generate integers randomly. Then, I perform required arithmetic operation on that integer. After that, I am checking if a HashMap is containing this Integer => If it contains this integer, I increment the integer and recheck if HashMap contains new integer until the integer is not present in HashMap. If it does not contain integer, I add integer to HashMap.
I added my code below. The code between 'start' and 'end' comments takes too long.
If I comment this code between 'start' and 'end', it executes in less than a second.
So, the time is not consumed in Random.nextInt() or HashMap.containsKey()
MyProgram.java
import java.util.HashMap;
import java.util.Random;
public class MyProgram {
public static void main(String[] args) {
long total = 0;
int randomInt;
int count = 100000;
int divider = 3;
Random random = new Random();
HashMap<Integer, Integer> map = new HashMap<>();
for(int i=0; i < count; i++){
randomInt = random.nextInt(count);
// start
int value1 = randomInt / divider;
int value2 = (randomInt % divider != 0) ? 1 : 0;
randomInt = value1 + value2;
// end
while(map.containsKey(randomInt)){
randomInt++;
}
map.put(randomInt, 0); // don't care about value
total += randomInt;
}
System.out.println("Total : " + total);
}
}
This implementation takes more than 30 seconds.
I can use List, Arrays, ArrayLists etc.. if you think they are fast. Please explain with an example.
Since you don't care about the value stored in the map, but rather just the integer key, just use a BitSet:
Replace the map with a BitSet of appropriate size:
BitSet bits = new BitSet(count);
Use the nextClearBit method to find the next unset bit:
randomInt = bits.nextClearBit(randomInt);
Then set that bit:
bits.set(randomInt);
This finishes very quickly for me (0.16s, in Ideone): Ideone demo.
I haven't measured it, but I would guess the main reason for the slowness of OP's code is the implicit object creation in the line:
while(map.containsKey(randomInt)){
Because maps contain references, randomInt has to be boxed to an Integer; only ints in the range -128 to 127 are guaranteed to be cached, so this will result in very large numbers of objects being created. BitSet avoids creating objects because it operates on primitive ints.
This is discussed in Effective Java 2nd Ed Item 5: "Avoid creating unnecessary objects" (look for the bit where it says "Hideously slow program!").
The problem isn't with the division-code as such, the problem is that you are generating much more collisions becaus you generate a smaller range for randomInt before trying to insert into map.
If you take a look at the following variation of your code
for(int i=0; i < count; i++){
randomInt = random.nextInt(33333);
// start
// int value1 = randomInt / divider;
// int value2 = (randomInt % divider != 0) ? 1 : 0;
// randomInt = value1 + value2;
// end
while(map.containsKey(randomInt)){
randomInt++;
}
map.put(randomInt, 0); // don't care about value
total += randomInt;
}
you will see that it takes about the same time as the code doing the divisions. So you should think over your insertion-strategy to see if you can improve that. (I can't help you with that since I have not fully understood what you are trying to achieve with your code).
A few trivial things:
use HashSet<> instead of Map, if you don't care about a value.
replace the part between comments with: randomInt = (randomInt + divider - 1) / divider;
These are minor things, but the part between the comments is not likely to be the major contributor to your performance issues.
Because you're generating 100K numbers in the range 0 <= n < 100K, your values are going to be very dense, so I expect you'll be iterating on the while quite a bit. You would probably be better of maintaining an array of Intervals as follows:
import java.util.HashSet;
import java.util.Random;
class Interval {
int min, max;
public Interval(int min, int max) {
this.min = min;
this.max = max;
}
public String toString() {
return "[" + min + "," + max + "]";
}
}
public class MyProgram {
private static void checkConsistency(Interval[] intv) {
for(int i=0; i<intv.length; i++) {
Interval v = intv[i];
if (v != null && (i < v.min || i > v.max)) {
throw new Error(i + " -> " + v);
}
}
}
public static void main(String[] args) {
long total = 0;
int randomInt;
int count = 100000;
int divider = 3;
Random random = new Random();
HashSet<Integer> hs = new HashSet<>();
Interval[] data = new Interval[count];
for(int i=0; i < count; i++){
randomInt = random.nextInt(count);
// start
randomInt = (randomInt + divider -1) / divider;
// end
Interval intv = data[randomInt];
if (intv != null) {
randomInt = intv.max + 1;
}
int idx = randomInt < count ? randomInt : count - 1;
hs.add(randomInt);
Interval pre = randomInt > 0 ? data[randomInt-1] : null;
Interval post = randomInt < count-1 ? data[randomInt+1] : null;
if (pre == null && post == null) {
data[idx] = new Interval(randomInt, randomInt);
} else if (pre != null && post != null) {
if (pre.max-pre.min < post.max-post.min) {
for (int j=pre.min; j <= pre.max; j++) {
data[j] = post;
}
data[idx] = post;
} else {
for (int j=post.min; j <= post.max; j++) {
data[j] = pre;
}
data[idx] = pre;
}
data[idx].min = pre.min;
data[idx].max = post.max;
} else if (pre != null) {
data[idx] = pre;
data[idx].max = randomInt;
} else {
data[idx] = post;
data[idx].min = randomInt;
}
// just for verifying consistency
checkConsistency(data);
total += randomInt;
}
System.out.println("Total : " + total);
}
}
The issue is with the inner while(map.containsKey(randomInt)) loop running for at least 3 Billion times (Look at the value of Entered variable in the output pasted below) due to huge collisions with the final number that gets generated after your calculation.
int value1 = randomInt / divider;
int value2 = (randomInt % divider != 0) ? 1 : 0;
randomInt = value1 + value2;
This code generates a lot of common values when looped for 100000 times and along with Auto-Boxing this could result in a performance issue.
You can check the amount of time the while loop is executed when collisions occur.
int randomInt;
int count = 100000;
int divider = 3;
long entered = 0;
Random random = new Random();
HashMap<Integer, Integer> map = new HashMap<>();
for(int i=0; i < count; i++){
randomInt = random.nextInt(count);
// start
int value1 = randomInt / divider;
int value2 = (randomInt % divider != 0) ? 1 : 0;
randomInt = value1 + value2;
// end
while(map.containsKey(randomInt)){
entered++;
randomInt++;
}
map.put(randomInt, 0); // don't care about value
total += randomInt;
}
System.out.println("Total : " + total);
System.out.println("Entered : " + entered);
Output :
Total : 4999950000
Entered : 3335662228
So you should revisit the logic of value1+value2 and rather use just random.nextInt(count) and give the count the range within which you want to generate.
randomInt = random.nextInt(count);
while(map.containsKey(randomInt)){
randomInt = random.nextInt(count);
}
If you don't care about the values (and of course, if you don't need it in your program), use a HashSet instead of a HashMap. It has the same behavior as the HashMap key list. There can't be duplicated values.
That means if your set already contains the value 102 and the next random integer generated is also 102, adding it to the set will do nothing. (For a Hashmap, it will just replace the key/value pair, but if your value is always 0, it won't be noticeable).
Thus, you don't need to check if your set contains the new random int and this part become useless:
while(map.containsKey(randomInt)){
randomInt++;
}
Plus, this part is time consumming as containsKey loop on the map, hence you can loop a loooots of time when there are many keys in your map (what happens if the incremented value is also in the list? and the next incremented value? and so on?).
That should makes you program a lot faster.
Now you can still optimize your code by removing the declaration of value1 and value2 but the time consumption is way lower than your whileloop so that probably won't make a big difference.
In the end, your code should looks like:
int randomInt;
int count = 100000;
int divider = 3;
Random random = new Random();
HashSet<Integer> set = new HashSet<>();
while(set.size()<count){
randomInt = random.nextInt(count);
// start
map.add((randomInt/divider) + ((randomInt % divider != 0) ? 1 : 0));
// end
}
Related
I am trying to write a method that creates and returns an array of random Integers that has a length of eight and is within the range [25, 725].
Every Integer in the array must be higher or lower than every other Integer in the array by at least 15. However, my method isn't returning arrays that meet this requirement.
I set up a main() method that checks the output of my method 100,000 times, and throws an Exception if there are any Integers that are too close.
How can I create a method that will return an array of Integers where the difference between every Integer and every other Integer is at least 15?
public class Test {
public static void main(String[] args) throws Exception {
Integer[] distances = new Integer[8];
for (int i = 0; i < 100000; i++) {
distances = createPlanetDistances(distances.length);
// check distances for values within 15
for (int x = 0; x < distances.length; x++) {
for (int y = 0; y < distances.length; y++) {
if (x == y)
continue;
if (Math.abs(distances[x] - distances[y]) < 15) {
System.out.println(distances[x] + " " + distances[y]);
throw new Exception("Doesn't work");
}
}
}
for (int distance : distances)
System.out.print(distance + " ");
System.out.println(System.lineSeparator());
}
}
/**
* Creates an array of distances-from-the-sun for a given number of Planets.
* It does not allow distances to be within 15 of any other distance.
*
* #param planetAmount The number of distances to return.
* #return An array of distances-from-the-sun.
*/
private static Integer[] createPlanetDistances(int planetAmount) {
SplittableRandom random = new SplittableRandom();
final int min = 25;
final int max = 726;
HashSet<Integer> distanceSet = new HashSet<>();
// make sure there are no duplicate Integers
for(int i = 0; i < planetAmount; i++) {
int num = random.nextInt(min, max);
while (distanceSet.contains(num))
num = random.nextInt(min, max);
distanceSet.add(num);
}
// make sure each distance is 15 away from all others
Integer[] distances = distanceSet.toArray(new Integer[]{});
for(int i = 0; i < distances.length; i++) {
// check distances[i] with all other Integers
for (int j = 0; j < distances.length; j++) {
// do not compare an element with itself
if (j == i)
continue;
int difference = Math.abs(distances[i] - distances[j]);
if (difference < 15) {
while (difference < 15) {
distances[i] = random.nextInt(min, max);
difference = Math.abs(distances[i] - distances[j]);
}
// check all Integers again
System.out.println("HERE " + i + " " + j);
i = 0;
break;
}
}
}
return distanceSet.toArray(new Integer[]{});
}
}
To find COUNT numbers in range MIN to MAX (exclusive) that are more than DISTANCE apart, build a TreeSet and use the ceiling(...) method to find nearby values.
Example
final int DISTANCE = 15, MIN = 25, MAX = 726, COUNT = 8;
ThreadLocalRandom random = ThreadLocalRandom.current();
TreeSet<Integer> numbers = new TreeSet<>();
while (numbers.size() < COUNT) {
int value = random.nextInt(MIN, MAX);
Integer ceiling = numbers.ceiling(value - DISTANCE);
if (ceiling == null || ceiling > value + DISTANCE)
numbers.add(value);
}
System.out.println(numbers);
Sample Output
[86, 104, 120, 369, 425, 532, 682, 713]
You can always shuffle the result if you don't want them in ascending order.
How It Works
The ceiling method returns the least value in the set greater than or equal to the given value, or null if there is no such value.
So if e.g. value is 134 and DISTANCE is 15, then ceiling(value - DISTANCE) will find the smallest value that is >= 119. If that value is >= 149, then we know the nearby range 119-149 is clear and we can use the 134 value.
You are generating planetary orbits, so it should be OK to have monotonically increasing numbers. Each number you generate has constraints imposed on it by the following numbers, and in turn imposes constraints on them once it is generated.
Constraint: If you want to generate N orbits between min and max separated by D, then your bounds for the first orbit are [min, max - D * (N - 1)]. This is simply because you can't pack the following N - 1 planets into a space that is less than D * (N - 1).
You can update the second constraint as you go, since the new minimum is going to be the last generated number + D. Here is a simple O(n) implementation (assuming that genrating a random number is O(1)):
final int DISTANCE = 15, MIN = 25, MAX = 726, COUNT = 8;
Random random = Random();
orbits = new int[COUNT];
if(MAX - MIN < DISTANCE * COUNT) {
throw new IllegalArgumentException("Insert pithy comment about COUNT");
}
min = MIN;
for(int i = 0; i < COUNT; i++) {
max = MAX - DISTANCE * (COUNT - i - 1);
orbits[i] = random.nextInt(max - min + 1) + min;
min = orbits[i] + DISTANCE;
}
The following approach avoids acceptance/rejection sampling by removing the spacing requirement, generating values uniformly over the correspondingly shortened range, adding the spacing gaps back, and shuffling to yield the results in a randomized order.
static Random r = new Random();
public static ArrayList<Integer>
gen_array(int lower_bound, int upper_bound, int n, int separation) {
upper_bound -= (n - 1) * separation;
if(upper_bound < lower_bound) {
throw new IllegalArgumentException("Infeasible arguments");
}
ArrayList<Integer> ary = new ArrayList<>();
while(ary.size() < n) {
ary.add(lower_bound + r.nextInt(upper_bound - lower_bound + 1));
}
Collections.sort(ary);
for (int i = 0; i < n; ++i) {
ary.set(i, ary.get(i) + i * separation);
}
Collections.shuffle(ary);
return ary;
}
If you call it with a value of 8 for n, a lower_bound of 25, an upper_bound of 130, and a separation of 15, it yields the correct result immediately where an acceptance/rejection approach could take an awful lot of iterations to cough up the unique set of values for the answer.
I have 2 ArrayLists. The first contains 993 float values. I have added every 12 instances of these 993 float values together and stored them in another ArrayList (993/12), giving the second ArrayList a size of 83.
Here is a snippet of the second ArrayList:
919.2, 927.9, 809.39996, 633.8, 626.8, 871.30005, 774.30005, 898.8, 789.6, 936.3, 765.4, 882.1, 681.1, 661.3, 847.9, 683.9, 985.7, 771.1, 736.6, 713.2001, 774.49994, ...
The first of these values i.e 919.2 corresponds to the year 1930.
The second, 927.9 corresponds to the year 1931.
The third, 809.39996 corresponds to the year 1932 and so on... meaning the last 83rd value will correspond to 2012.
The ultimate aim I have is to look at these values in this second ArrayList and find the largest, printing its value AND the year that corresponds with it.
The issue is the program currently has no way of knowing these corresponding year values.
Allowed assumption: the first corresponding year value is 1930.
Currently I am able to successfully print the largest value in the ArrayList which is half the problem.
To achieve this is simply sorted the ArrayList:
System.out.println(depthAdd.get(depthAdd.size() -1));
What im lacking is the corresponding year. How can I do this???
Here is some code for you:
public void depthValues() {
ArrayList<Float> rValue = new ArrayList<>();
...
ArrayList<Float> depthAdd = new ArrayList<>();
Iterator<Float> it = rValue.iterator();
final int MAX = 12;
while(it.hasNext()) {
float sum = 0f;
int counter = 1;
while (counter <= MAX && it.hasNext()) {
sum += it.next();
counter++;
}
depthAdd.add(sum);
}
Collections.sort(depthAdd);
System.out.println("Wettest year: //year needs to go here "
+ depthAdd.get(depthAdd.size() -1));
return;
}
You could copy the original List (before sorting). And then iterate again to determine matching position(s). Another option is to create a custom class to contain the year and the value, and create a custom Comparator on the ammount of rainfall. The first might be implemented like
List<Float> depthCopy = new ArrayList<>(depthAdd);
Collections.sort(depthAdd);
Float max = depthAdd.get(depthAdd.size() - 1);
for (int i = 0; i < depthCopy.size(); i++) {
if (depthCopy.get(i).equals(max)) {
System.out.println("Wettest year: " + (1930 + i) + " "
+ depthAdd.get(depthAdd.size() - 1));
}
}
Just keep track of the index and maximum value in separate variables:
float maxValue;
int maxIndex;
while (it.hasNext()) {
float sum = 0f;
int counter = 1;
while (counter <= MAX && it.hasNext()) {
sum += it.next();
counter++;
}
if (sum > maxValue) {
maxValue = sum;
maxIndex = depthAdd.size(); // The current index
}
depthAdd.add(sum);
}
Then print the value:
System.out.println("The max was " + maxValue + " in " + (1930 + maxIndex));
If you need/want to store the value pairs you could use something like Map<Integer, Float> and directly reference the key value pair after sorting. Otherwise, either of the previous answers should work.
Float max = depthAdd.get(0);
int index = 0;
for (int i = 1; I < r.size(); i++)
if (depthAdd.get(i) > max) {
max = depthAdd.get(i);
index = i;
}
int year = 1930 + index;
And max will be the biggest value;
It sounds like you want a key,value pair. That's a job for a Map, not a List.
Map<Integer,Float> depthAdd = new HashMap<Integer,Float>();
...
int year = 1930;
while(it.hasNext()) {
float sum = 0f;
int counter = 1;
while (counter <= MAX && it.hasNext()) {
sum += it.next();
counter++;
}
depthAdd.put(year, sum);
year++;
}
...
I have this so far:
public static void highV()
{
KeyboardReader reader = new KeyboardReader();
int numVal = 0;
while (numVal < 3) // Makes sure 3 or more numbers are entered
{
numVal = reader.readInt("How many values would you like to enter (3 or more): ");
if (numVal < 3)
{
System.out.println("Invalid Entry");
}
}
int[] dval = new int[numVal];
int i;
int j;
int k;
int a;
int high = 0;
int sec = 0;
int thr = 0;
System.out.println();
for (i = 0; i < dval.length; i++) // Reads in numbers and stores them in an array
{
dval[i] = reader.readInt("Enter value number " + (i + 1) + ". ");
}
System.out.println();
System.out.print("List of values: ");
for (j = 0; j < dval.length; j++)// Prints out a list of values
{
if (j == (dval.length)-1)
{
System.out.println(dval[j]);
}
else
{
System.out.print(dval[j] + ", ");
}
}
System.out.println();
System.out.println("There was a total of " + dval.length + " numbers entered.");
System.out.println();
for (k = 0; k < dval.length; k++) // Determines the highest second highest and third highest numbers
{
if (dval[k] > high)
{
int oldSec = sec;
sec = high;
thr = oldSec;
high = dval[k];
}
else if (dval[k] > sec)
{
thr = sec;
sec = dval[k];
}
else if (dval[k] > thr)
{
thr = dval[k];
}
}
for (a = 0; a < dval.length; a++) // Determines sequence location of first second and third highest numbers
{
if (dval[a] == high)
{
high = a+1;
}
if (dval[a] == sec)
{
sec = a+1;
}
if (dval[a] == thr)
{
thr = a+1;
}
}
System.out.println("The highest number was in sequence #: " + high);
System.out.println("The second highest number was in sequence #: " + sec);
System.out.println("The third highest number was in sequence #: " + thr);
System.out.println();
}
This works for almost everything, except when the numbers entered are all descending. Example: If you enter 5,4,3,2,1 you get 5,4,3 as answers when you should get 1,2,3.
If you enter 2,18,5,3,1,0,9,100 however you get the correct answer of 8,2,7
Any ideas?
this block might be problematic because you're repurposing high, sec, and thr from representing the values of the array to representing the index of the array.
Not only that, but you're depending on high, sec, and thr, being values of the array throughout the loop.
for (a = 0; a < dval.length; a++) // Determines sequence location of first second and third highest numbers
{
if (dval[a] == high)
{
high = a+1;
}
if (dval[a] == sec)
{
sec = a+1;
}
if (dval[a] == thr)
{
thr = a+1;
}
}
after the first iteration, high will be 5,(correct), but you'll set it to 1 which you want to display in your output.
But when you come through the second iteration, and high is 1, and a, is 1, the condition: (dval[a] == high) will be true, but in error, and similar erros will happen throughout that loop.
I would Strongly advise using different variables to keep track of the indices of your values than the ones that you use to keep track of your values.
if (dval[a] == high)
{
high = a+1;
}
if (dval[a] == sec)
{
sec = a+1;
}
if (dval[a] == thr)
{
thr = a+1;
}
When you're determining the indexes for them, you're reusing the same variable. In the 5, 4, 3, 2, 1 case high is first set to 1 which will match stuff later.
Introduce 3 new variables highInd, secInd, and thrInd and that should fix your issue.
Above the for loop:
int highInd=0;
int secInd=0;
int thrInd=0;
In the for loop:
if (dval[a] == high)
{
highInd = a+1;
}
if (dval[a] == sec)
{
secInd = a+1;
}
if (dval[a] == thr)
{
thrInd = a+1;
}
Try this. When you're printing, change the variable names to these.
Took me a bit to write this up, but why not handle all of your processing at once. Get your index values and actual values all in one go. Roll the values down if you find one that is bigger and keep on trucking through the for loop.
This code isn't tested in full swing, but more of an example to show rolling values down as you find a larger value.
int firstLargest = Integer.MIN_VALUE;
int secondLargest = Integer.MIN_VALUE;
int thirdLargest = Integer.MIN_VALUE;
int firstLargestIndex = -1;
int secondLargestIndex = -1;
int thirdLargestIndex = -1;
// loop through array, check for a higher value than values
// that have already been saved, if necessary, roll the values down
// and save the current value
for (j = 0; j < dval.length; j++) {
if(dval[j] > firstLargest) {
thirdLargestIndex = secondLargestIndex;
secondLargestIndex = firstLargestIndex;
firstLargestIndex = j;
thirdLargest = secondLargest;
secondLargest = firstLargest;
firstLargest = dval[j];
} else if(dval[j] > secondLargest) {
thirdLargestIndex = secondLargestIndex;
secondLargestIndex = j;
thirdLargest = secondLargest;
secondLargest = dval[j];
} else if(dval[j] > thirdLargest) {
thirdLargestIndex = j;
thirdLargest - dval[j];
}
}
If you're interested in an alternative approach, you could create a Map mapping the numbers to their indexes. E.g.
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < numList.Length; i++)
map.put(numList[i], i);
List<Integer> sortedList = // get descending list of keys from map
for (int i = 0; i < 3; i++)
System.out.println(String.valueOf(numList.get(i) + 1));
I like this solution better because it is shorter and thus, IMHO, more readable and easy to debug. It's probably a little slower than yours could be, since it's having other classes do some extra work.
Create LinkedList
Sort
Pop 3 first items
Find their id in original array
Integer data[] = new Integer[] {10,20,30,40,50,60,71,80,90,91 };
ArrayList<Integer> originalList = new ArrayList<Integer>(Arrays.asList(data));
LinkedList<Integer> sortedList = new LinkedList<Integer>(Arrays.asList(data));
Collections.sort(sortedList,Collections.reverseOrder());
Integer biggest = sortedList.pop();
Integer second = sortedList.pop();
Integer third = sortedList.pop();
Integer indexOfBiggest = originalList.indexOf(biggest);
Integer indexOfSecond = originalList.indexOf(second);
Integer indexOfThird = originalList.indexOf(third);
just Ideas :
1- use Integer instead of int
2- create 2 comparators for the sorting order you wish to get the first 3 of
3- use LinkedList and PriorityQueue instead of arrays
to be frank with you i didnt get your point quite well, but for getting the first three that the user entered, you can store the values he enters in a Linked list and get the first three
if you have a certain equation to sort his enteries, you can create a comparator (class that implements the comparator interface) and use an instance of it while creating a priority queue for example (or Sort your list with this comparator) and get the first three elements
if you can clarify more your situation i can help
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.
Note: Version 2, below, uses the Sieve of Eratosthenes. There are several answers that helped with what I originally asked. I have chosen the Sieve of Eratosthenes method, implemented it, and changed the question title and tags appropriately. Thanks to everyone who helped!
Introduction
I wrote this fancy little method that generates an array of int containing the prime numbers less than the specified upper bound. It works very well, but I have a concern.
The Method
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
int [] primes = new int [index];
while(--index >= 0) {
primes [index] = temp [index];
}
return primes;
}
My Concern
My concern is that I am creating an array that is far too large for the final number of elements the method will return. The trouble is that I don't know of a good way to correctly guess the number of prime numbers less than a specified number.
Focus
This is how the program uses the arrays. This is what I want to improve upon.
I create a temporary array that is
large enough to hold every number
less than the limit.
I generate the prime numbers, while
keeping count of how many I have
generated.
I make a new array that is the right
dimension to hold just the prime
numbers.
I copy each prime number from the
huge array to the array of the
correct dimension.
I return the array of the correct
dimension that holds just the prime
numbers I generated.
Questions
Can I copy the whole chunk (at once) of
temp[] that has nonzero
elements to primes[]
without having to iterate through
both arrays and copy the elements
one by one?
Are there any data structures that
behave like an array of primitives
that can grow as elements are added,
rather than requiring a dimension
upon instantiation? What is the
performance penalty compared to
using an array of primitives?
Version 2 (thanks to Jon Skeet):
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
return Arrays.copyOfRange(temp, 0, index);
}
Version 3 (thanks to Paul Tomblin) which uses the Sieve of Erastosthenes:
private static int [] generatePrimes(int max) {
boolean[] isComposite = new boolean[max + 1];
for (int i = 2; i * i <= max; i++) {
if (!isComposite [i]) {
for (int j = i; i * j <= max; j++) {
isComposite [i*j] = true;
}
}
}
int numPrimes = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) numPrimes++;
}
int [] primes = new int [numPrimes];
int index = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) primes [index++] = i;
}
return primes;
}
Your method of finding primes, by comparing every single element of the array with every possible factor is hideously inefficient. You can improve it immensely by doing a Sieve of Eratosthenes over the entire array at once. Besides doing far fewer comparisons, it also uses addition rather than division. Division is way slower.
ArrayList<> Sieve of Eratosthenes
// Return primes less than limit
static ArrayList<Integer> generatePrimes(int limit) {
final int numPrimes = countPrimesUpperBound(limit);
ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
boolean [] isComposite = new boolean [limit]; // all false
final int sqrtLimit = (int)Math.sqrt(limit); // floor
for (int i = 2; i <= sqrtLimit; i++) {
if (!isComposite [i]) {
primes.add(i);
for (int j = i*i; j < limit; j += i) // `j+=i` can overflow
isComposite [j] = true;
}
}
for (int i = sqrtLimit + 1; i < limit; i++)
if (!isComposite [i])
primes.add(i);
return primes;
}
Formula for upper bound of number of primes less than or equal to max (see wolfram.com):
static int countPrimesUpperBound(int max) {
return max > 1 ? (int)(1.25506 * max / Math.log((double)max)) : 0;
}
Create an ArrayList<Integer> and then convert to an int[] at the end.
There are various 3rd party IntList (etc) classes around, but unless you're really worried about the hit of boxing a few integers, I wouldn't worry about it.
You could use Arrays.copyOf to create the new array though. You might also want to resize by doubling in size each time you need to, and then trim at the end. That would basically be mimicking the ArrayList behaviour.
Algo using Sieve of Eratosthenes
public static List<Integer> findPrimes(int limit) {
List<Integer> list = new ArrayList<>();
boolean [] isComposite = new boolean [limit + 1]; // limit + 1 because we won't use '0'th index of the array
isComposite[1] = true;
// Mark all composite numbers
for (int i = 2; i <= limit; i++) {
if (!isComposite[i]) {
// 'i' is a prime number
list.add(i);
int multiple = 2;
while (i * multiple <= limit) {
isComposite [i * multiple] = true;
multiple++;
}
}
}
return list;
}
Image depicting the above algo (Grey color cells represent prime number. Since we consider all numbers as prime numbers intially, the whole is grid is grey initially.)
Image Source: WikiMedia
The easiest solution would be to return some member of the Collections Framework instead of an array.
Are you using Java 1.5? Why not return List<Integer> and use ArrayList<Integer>? If you do need to return an int[], you can do it by converting List to int[] at the end of processing.
As Paul Tomblin points out, there are better algorithms.
But keeping with what you have, and assuming an object per result is too big:
You are only ever appending to the array. So, use a relatively small int[] array. When it's full use append it to a List and create a replacement. At the end copy it into a correctly sized array.
Alternatively, guess the size of the int[] array. If it is too small, replace by an int[] with a size a fraction larger than the current array size. The performance overhead of this will remain proportional to the size. (This was discussed briefly in a recent stackoverflow podcast.)
Now that you've got a basic sieve in place, note that the inner loop need only continue until temp[i]*temp[i] > prime.
I have a really efficient implementation:
we don't keep the even numbers, therefore halving the memory usage.
we use BitSet, requiring only one bit per number.
we estimate the upper bound for number of primes on the interval, thus we can set the initialCapacity for the Array appropriately.
we don't perform any kind of division in the loops.
Here's the code:
public ArrayList<Integer> sieve(int n) {
int upperBound = (int) (1.25506 * n / Math.log(n));
ArrayList<Integer> result = new ArrayList<Integer>(upperBound);
if (n >= 2)
result.add(2);
int size = (n - 1) / 2;
BitSet bs = new BitSet(size);
int i = 0;
while (i < size) {
int p = 3 + 2 * i;
result.add(p);
for (int j = i + p; j < size; j += p)
bs.set(j);
i = bs.nextClearBit(i + 1);
}
return result;
}
Restructure your code. Throw out the temporary array, and instead write function that just prime-tests an integer. It will be reasonably fast, since you're only using native types. Then you can, for instance, loop and build a list of integers that are prime, before finally converting that to an array to return.
Not sure if this will suite your situation but you can take a look at my approach. I used mine using Sieve of Eratosthenes.
public static List<Integer> sieves(int n) {
Map<Integer,Boolean> numbers = new LinkedHashMap<>();
List<Integer> primes = new ArrayList<>();
//First generate a list of integers from 2 to 30
for(int i=2; i<n;i++){
numbers.put(i,true);
}
for(int i : numbers.keySet()){
/**
* The first number in the list is 2; cross out every 2nd number in the list after 2 by
* counting up from 2 in increments of 2 (these will be all the multiples of 2 in the list):
*
* The next number in the list after 2 is 3; cross out every 3rd number in the list after 3 by
* counting up from 3 in increments of 3 (these will be all the multiples of 3 in the list):
* The next number not yet crossed out in the list after 5 is 7; the next step would be to cross out every
* 7th number in the list after 7, but they are all already crossed out at this point,
* as these numbers (14, 21, 28) are also multiples of smaller primes because 7 × 7 is greater than 30.
* The numbers not crossed out at this point in the list are all the prime numbers below 30:
*/
if(numbers.get(i)){
for(int j = i+i; j<n; j+=i) {
numbers.put(j,false);
}
}
}
for(int i : numbers.keySet()){
for(int j = i+i; j<n && numbers.get(i); j+=i) {
numbers.put(j,false);
}
}
for(int i : numbers.keySet()){
if(numbers.get(i)) {
primes.add(i);
}
}
return primes;
}
Added comment for each steps that has been illustrated in wikipedia
I have done using HashMap and found it very simple
import java.util.HashMap;
import java.util.Map;
/*Using Algorithms such as sieve of Eratosthanas */
public class PrimeNumber {
public static void main(String[] args) {
int prime = 15;
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
hashMap.put(0, 0);
hashMap.put(1, 0);
for (int i = 2; i <= prime; i++) {
hashMap.put(i, 1);// Assuming all numbers are prime
}
printPrimeNumberEratoshanas(hashMap, prime);
}
private static void printPrimeNumberEratoshanas(HashMap<Integer, Integer> hashMap, int prime) {
System.out.println("Printing prime numbers upto" + prime + ".....");
for (Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
if (entry.getValue().equals(1)) {
System.out.println(entry.getKey());
for (int j = entry.getKey(); j < prime; j++) {
for (int k = j; k * j <= prime; k++) {
hashMap.put(j * k, 0);
}
}
}
}
}
}
Think this is effective
public static void primes(int n) {
boolean[] lista = new boolean[n+1];
for (int i=2;i<lista.length;i++) {
if (lista[i]==false) {
System.out.print(i + " ");
}
for (int j=i+i;j<lista.length;j+=i) {
lista[j]=true;
}
}
}