I have this code:
public static void main(String[] args) {
final int[] weights = {20,40,10,30}, costs = {5,20,2,6};
final int minWeight = 50;
firstSolution(weights,costs,minWeight);
}
public static void firstSolution(int[] weights, int[] costs, int minWeight){
int maxWeight = 0;
for(final int weight: weights){
maxWeight += weight;
}
int[] minCost = new int[maxWeight + 1];
for(int i = 1; i <= maxWeight; i++){
minCost[i] = Integer.MAX_VALUE;
}
for(int i = 0; i < weights.length; i++){
for(int j = maxWeight; j >= weights[i]; j--){
if(minCost[j - weights[i]] != Integer.MAX_VALUE){
minCost[j] = Math.min(minCost[j], minCost[j - weights[i]] + costs[i]);
}
}
}
int answer = Integer.MAX_VALUE;
for(int i = minWeight; i <= maxWeight; i++){
answer = Math.min(answer, minCost[i]);
}
System.out.println(answer);
}
This codes takes as input an array of weights and an array of costs, and it calculates the least possible cost for the given minimum Weight. I actually need to also have which item's are used for this solution.
For example with these inputs i would have as my optimal solution :
Use item at index 0 (weight = 20, cost = 5) and item at index 3(weight = 30, cost = 6).
This will give me the minimum cost which is 11 for more than or equal weight which is 50 in this case.
The code works and gives me answer 11 which is the minimum cost, but it doesn't give me the actual items that led to this solutions. Could you help with changing the code a bit so it can also determine which items lead to the optimal solution?
When you do the following:
minCost[j] = Math.min(minCost[j], minCost[j - weights[i]] + costs[i]);
you don't know if the existing solution or the new solution are the best one, so instead you should do:
if (minCost[j - weights[i]] + costs[i] < minCost[j]) {
minCost[j] = minCost[j - weights[i]] + costs[i];
// And update the storage of the best solution here.
}
Now to store a best solution, you only need to know what was your last best choice, and then iterate / recurse backwards to reconstruct the solution.
For instance, in the above code you know your optimal solution includes item i. So you can simply update your best solution with the following code:
solutions[j] = i;
And then when you're done you can always reconstruct your solution knowing that it was built on solutions[j - weights[solutions[j]]], repeating this backtracking until -1 == solutions[j].
Putting this all together, we get:
while (-1 != solution[W]) {
// This prints the last item picked and its corresponding weight.
print solution[W] + ": " + weight[solution[W]]
// This updates the total weight to refer to the
// optimal sub-solution that we built upon.
W = W - weight[solution[W]]
}
Related
Please refer to this problem from Hackerrank
HackerLand National Bank has a simple policy for warning clients about possible fraudulent account activity. If the amount spent by a client on a particular day is greater than or equal to the client's median spending for a trailing number of days, they send the client a notification about potential fraud. The bank doesn't send the client any notifications until they have at least that trailing number of prior days' transaction data.
I have written the following code. However, the code is working for some of the test cases and is getting 'terminated due to timeout' for some. Can anyone please tell how can I improve the code?
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations,itr,length,median,midDummy,midL,midR, midDummy2,i,i1,temp,count;
float mid,p,q;
length = expenditure.length;
iterations = length-d;
i=0;
i1=0;
itr=0;
count = 0;
int[] exSub = new int[d];
while(iterations>0)
{
// Enter the elements in the subarray
while(i1<d)
{
exSub[i1]=expenditure[i+i1];
//System.out.println(exSub[i1]);
i1++;
}
//Sort the exSub array
for(int k=0; k<(d-1); k++)
{
for(int j=k+1; j<d; j++)
{
if(exSub[j]<exSub[k])
{
temp = exSub[j];
exSub[j] = exSub[k];
exSub[k] = temp;
}
}
}
//Printing the exSub array in each iteration
for(int l = 0 ; l<d ; l++)
{
System.out.println(exSub[l]);
}
i1=0;
//For each iteration claculate the median
if(d%2 == 0) // even
{
midDummy = d/2;
p= (float)exSub[midDummy];
q= (float)exSub[midDummy-1];
mid = (p+q)/2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 =d/2;
mid=exSub[midDummy2];
//System.out.println(midDummy2);
}
if(expenditure[itr+d]>=2*mid)
{
count++;
}
itr++;
i++;
iterations--;
System.out.println("Mid:"+mid);
System.out.println("---------");
}
System.out.println("Count:"+count);
return count;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
}
}
The first rule on performance improvement is: Don't improve the performance if it's not needed.
Performance improvements usually lead to code that is less readable and therefore it should only be done when it's really needed.
The second rule is: Improve algorithms and data-structures before low-level improvements.
If you need to improve the performance of your code always try to use more efficient algorithms and data-structures before going to low-level improvement. In your code example that would be: Don't use BubbleSort, but try to use more efficient algorithms like Quicksort or Mergesort, because they use time complexity of O(n*log(n) while Bubble sort has a time complexity of O(n^2) which is much slower when you have to sort big arrays. You can use Arrays.sort(int[]) to do this.
Your data-structures are only arrays so this can't be improved in your code.
This will give your code quite some speedup, and will not lead to a code that can't be read anymore. Improvements like changing simple calculations to slightly faster calculations using bitshifts and other fast calculations (that are pretty hard to understand if used to often) will almost always lead to a code that is only slightly faster but no one will be able to easily understand it anymore.
Some improvements that could be applied to your code (that will also only slightly improve the performance) are:
Replace while loops with for loops if possible (they can be improved by the compiler)
Don't use System.out.println for many texts if it's not totaly needed (because it's quite slow for big texts)
Try to copy arrays using System.arraycopy which usually is faster than copying using while loops
So an improved code of yours could look like this (I marked the changed parts with comments):
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations, itr, length, median, midDummy, midL, midR, midDummy2, i, i1, temp, count;
float mid, p, q;
length = expenditure.length;
iterations = length - d;
i = 0;
i1 = 0;
itr = 0;
count = 0;
int[] exSub = new int[d];
//EDIT: replace while loops with for loops if possible
//while (iterations > 0) {
for (int iter = 0; iter < iterations; iter++) {
//EDIT: here you can again use a for loop or just use System.arraycopy which should be (slightly) fasters
// Enter the elements in the subarray
/*while (i1 < d) {
exSub[i1] = expenditure[i + i1];
//System.out.println(exSub[i1]);
i1++;
}*/
System.arraycopy(expenditure, i, exSub, 0, d);
//EDIT: Don't use bubble sort!!! It's one of the worst sorting algorithms, because it's really slow
//Bubble sort uses time complexity O(n^2); others (like merge-sort or quick-sort) only use O(n*log(n))
//The easiest and fastest solution is: don't implement sorting by yourself, but use Arrays.sort(int[]) from the java API
//Sort the exSub array
/*for (int k = 0; k < (d - 1); k++) {
for (int j = k + 1; j < d; j++) {
if (exSub[j] < exSub[k]) {
temp = exSub[j];
exSub[j] = exSub[k];
exSub[k] = temp;
}
}
}*/
Arrays.sort(exSub);
//Printing the exSub array in each iteration
//EDIT: printing many results also takes much time, so only print the results if it's really needed
/*for (int l = 0; l < d; l++) {
System.out.println(exSub[l]);
}*/
i1 = 0;
//For each iteration claculate the median
if (d % 2 == 0) // even
{
midDummy = d / 2;
p = (float) exSub[midDummy];
q = (float) exSub[midDummy - 1];
mid = (p + q) / 2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 = d / 2;
mid = exSub[midDummy2];
//System.out.println(midDummy2);
}
if (expenditure[itr + d] >= 2 * mid) {
count++;
}
itr++;
i++;
//iterations--;//EDIT: don't change iterations anymore because of the for loop
System.out.println("Mid:" + mid);
System.out.println("---------");
}
System.out.println("Count:" + count);
return count;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
}
}
Edit:
You can make the solution even faster if you don't sort the complete (sub-)array in every iteration, but instead only remove one value (the first day that is not used anymore) and add a new value (the new day that is now used) in the correct position (like #Vojtěch Kaiser mentioned in his answer)
This will make it even faster, because sorting an array takes the time O(d*log(d)), while adding a new value into an array, that is already sorted only takes the time O(log(d)) if you are using a search tree. When using an array (like I did in the example below) it takes the time O(d) because when using an array you need to copy the array values which takes linear time (like #dyukha mentioned in the comments). So the improvement (again) can be done by using a better algorithm (This solution could also be improved by using a search tree instead of an array).
So the new solution could look like this:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class Solution {
// Complete the activityNotifications function below.
static int activityNotifications(int[] expenditure, int d) {
//Delaring Variables
int iterations, length, midDummy, midDummy2, count;//EDIT: removed some unused variables here
float mid, p, q;
length = expenditure.length;
iterations = length - d;
count = 0;
//EDIT: add the first d values to the sub-array and sort it (only once)
int[] exSub = new int[d];
System.arraycopy(expenditure, 0, exSub, 0, d);
Arrays.sort(exSub);
for (int iter = 0; iter < iterations; iter++) {
//EDIT: don't sort the complete array in every iteration
//instead remove the one value (the first day that is not used anymore) and add the new value (of the new day) into the sorted array
//sorting is done in O(n * log(n)); deleting and inserting a new value into a sorted array is done in O(log(n))
if (iter > 0) {//not for the first iteration
int remove = expenditure[iter - 1];
int indexToRemove = find(exSub, remove);
//remove the index and move the following values one index to the left
exSub[indexToRemove] = 0;//not needed; just to make it more clear what's happening
System.arraycopy(exSub, indexToRemove + 1, exSub, indexToRemove, exSub.length - indexToRemove - 1);
exSub[d - 1] = 0;//not needed again; just to make it more clear what's happening
int newValue = expenditure[iter + d - 1];
//insert the new value to the correct position
insertIntoSortedArray(exSub, newValue);
}
//For each iteration claculate the median
if (d % 2 == 0) // even
{
midDummy = d / 2;
p = exSub[midDummy];
q = exSub[midDummy - 1];
mid = (p + q) / 2;
//mid = (exSub[midDummy]+exSub [midDummy-1])/2;
//System.out.println(midDummy);
}
else // odd
{
midDummy2 = d / 2;
mid = exSub[midDummy2];
//System.out.println(midDummy2);
}
if (expenditure[iter + d] >= 2 * mid) {
count++;
}
}
System.out.println("Count:" + count);
return count;
}
/**
* Find the position of value in expenditure
*/
private static int find(int[] array, int value) {
int index = -1;
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
index = i;
}
}
return index;
}
/**
* Find the correct position to insert value into the array by bisection search
*/
private static void insertIntoSortedArray(int[] array, int value) {
int[] indexRange = new int[] {0, array.length - 1};
while (indexRange[1] - indexRange[0] > 0) {
int mid = indexRange[0] + (indexRange[1] - indexRange[0]) / 2;
if (value > array[mid]) {
if (mid == indexRange[0]) {
indexRange[0] = mid + 1;
}
else {
indexRange[0] = mid;
}
}
else {
if (mid == indexRange[1]) {
indexRange[1] = mid - 1;
}
else {
indexRange[1] = mid;
}
}
}
System.arraycopy(array, indexRange[0], array, indexRange[0] + 1, array.length - indexRange[0] - 1);
array[indexRange[0]] = value;
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String[] nd = scanner.nextLine().split(" ");
int n = Integer.parseInt(nd[0]);
int d = Integer.parseInt(nd[1]);
int[] expenditure = new int[n];
String[] expenditureItems = scanner.nextLine().split(" ");
scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
for (int i = 0; i < n; i++) {
int expenditureItem = Integer.parseInt(expenditureItems[i]);
expenditure[i] = expenditureItem;
}
int result = activityNotifications(expenditure, d);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedWriter.close();
scanner.close();
//Just for testing; can be deleted if you don't need it
/*int[] exp = new int[] {2, 3, 4, 2, 3, 6, 8, 4, 5};
int d = 5;
activityNotifications(exp, d);
int[] exp2 = new int[] {1, 2, 3, 4, 4};
d = 4;
activityNotifications(exp2, d);*/
}
}
Your main concern is that you are sorting the partial array in every iteration, costing you total complexity of the problem O(n d log(d)), which can get pretty hairy for large d values.
What you want is to keep the array sorted between iterations and sort in/out changed values. For that you would implement binary search tree (BST) or some other balanced option (AVL, ...), perform O(log(d)) removal of oldest value, then perform O(log(d)) insertion of new value, and simply look in the middle for median. Total asymptotic complexity would be O(n log(d)) which is as far as I know the best you can get - rest of the optimization is low level dirty work.
Take a look at java https://docs.oracle.com/javase/10/docs/api/java/util/TreeSet.html, which should take care of the most of the work, but keep in mind that underlying structure is made out of objects that will be slower than arrays.
I was given a programming question by a friend where I was given a 1000 character string of numbers. The task is to find the largest product of 30 consecutive digits.
Although my code looks right, the answer seems to come out really low, why is this?
The relevant code for this problem has been provided below.
static String s = "2389748943892"; //... This number is actually 1000 characters.
public static void main(String[] args) {
// TODO Auto-generated method stub
int largest = 0;
for(int i = 0; i < 970; i ) {
String cur = s.substring(i, i 30);
int cur_val = 0;
for(int x = 0; x < 30; x ) {
if(x == 0) {
System.out.println(Integer.parseInt(cur.substring(0, 1)));
cur_val = Integer.parseInt(cur.substring(x, 1));
} else {
cur_val = cur_val * Integer.parseInt(cur.substring(x, x 1));
}
}
if(cur_val > largest) {
largest = cur_val;
}
System.out.println("Largest: " largest);
// should be 8876473335152640000 but is 2013265920
}
}
Edit: Arrgh, I read 'slow' instead of 'low'...
OK, forget about the performance issues, I thought you were speaking of.
Howver, long won't help you either: calculate ln(9^30)/ln(2), and you get little more than 95, thus you need 96 digits. Try Math.BigInteger then!
This is due to the excessive usage of substring (resulting in constructing and destroying new String objects all the time). As you are only interested in single characters, better use s.charAt(n). Parsing is then easy, too: Just subtract '0' from the char you got this way. So you get:
for(int i = 0; i < 970; ++i)
{
int cur_val = 0;
for(int x = i; x < i + 30; ++x)
{
cur_val *= s.charAt(x) - '0';
}
}
/* rest as you had already */
(OK, I left out printing the substring).
Additionally, in the code you posted, there are some syntax errors (substring i, i 30, apparently there is missing a '+') and you did not increment the counter variables (if this happened in your code, too, you would have ended in endless loops - but then you would not have gotten a slow result, you would not have gotten any at all).
You might include some short cuts (e. g. if you find a '0' in the outer loop, you know that the result for the next 30 iterations will be 0 and you could skip these), giving you another speedup.
I was trying figure out why the below solution failed for a single performance test case for the 'Max Double Slice Sum' problem in the codility website: https://codility.com/demo/take-sample-test/max_double_slice_sum
There is another solution O(n) space complexity which is easier to comprehend overhere: Max double slice sum. But i am just wondering why this O(1) solution doesn't work. Below is the actual code:
import java.util.*;
class Solution {
public int solution(int[] A) {
long maxDS = 0;
long maxDSE = 0;
long maxS = A[1];
for(int i=2; i<A.length-1; ++i){
//end at i-index
maxDSE = Math.max(maxDSE+A[i], maxS);
maxDS = Math.max(maxDS, maxDSE);
maxS = Math.max(A[i], maxS + A[i]);
}
return (int)maxDS;
}
}
The idea is simple as follow:
The problem can be readdress as finding max(A[i]+A[i+1]+...+A[j]-A[m]); 1<=i<=m<=j<=n-2; while n = A.length; we call A[m] is missing element within the slice.
maxS[i] will keep max slice which end at current index i; in other words, = max(A[t] + ... + A[i]); while t < i; so when i=1; maxS = A[1]; Note that in solution, we don't keep array but rather latest maxS at current index (See above code).
maxDSE[i] is max of all double slice which end at i; in other words, = max(A[t]+A[t+1]+...+A[i]-A[m])--end at A[i]; maxDS is the final max of double slice sum which we try to find.
Now, we just use a for-loop from i=2; -> i=A.length-2; For each index i, we notice some findings:
If the missing element is A[i], then maxDSE[i] = maxS[i-1] (max sum of
all slice which end at i-1 => or A[t] + ... + A[i] - A[i]);
If missing element is not A[i] -> so it must be somewhere from A[1]->A[i-1] -> maxDSE = maxDSE[i-1] + A[i]; such as A[t] + ... + A[i] - A[m] (not that A[i] must be last element) with t
so maxDSE[i] = Math.max(maxDSE[i-1]+A[i], maxS[i-1]);
maxDS = Math.max(maxDS, maxDSE); max amount all maxDSE;
and maxS[i] = Math.max(A[i], maxS[i-1]+A[i]);
by that way, maxDS will be the final result.
But strange that, I was only able to get 92%; with one failed performance test case as shown here:
medium_range
-1000, ..., 1000
WRONG ANSWER
got 499499 expected 499500
Could anyone please enlighten me where is problem in my solution? Thanks!
Ok, I found the error with my code. Seems that I forgot one corner cases. When calculate DSE[i], in cases A[i] is missing number, maxS should contain the case when array is empty. In other word, maxS should be calculated as:
maxS[i] = Math.max(0, Math.max(A[i]+maxS[i-1], A[i])); while 0 is for case of empty subarray (end at i-th); Math.max(A[i]+maxS[i-1], A[i]) is max of all slice with at least one element (end at i-index). The complete code as follow:
import java.util.*;
class Solution {
public int solution(int[] A) {
long maxDS = 0;
long maxDSE = 0;
long maxS = A[1];
for(int i=2; i<A.length-1; ++i){
maxDSE = Math.max(maxDSE+A[i], maxS);
maxDS = Math.max(maxDS, maxDSE);
maxS = Math.max(0, Math.max(A[i], maxS + A[i]));
}
return (int)maxDS;
}
}
It seems that for the input [-11, -53, -4, 38, 76, 80], your solution doesn't work. Yes, it tricks all the codility test cases, but I managed to trick all codility test cases for other problems too.
If you don't just want to trick codility, but also you want to come with a good solution, I suggest that you create a loop and a large number of random test cases (in number of elements and element values), and create a test method of your own, that you are sure works (even if the complexity is quadratic), compare the results from both methods and then analyze the current random input that doesn't fit.
Here is clear solution. Best approach is to use algorithm of Kanade O(N) and O(1) by space
public class DuplicateDetermineAlgorithm {
public static boolean isContainsDuplicate(int[] array) {
if (array == null) {
throw new IllegalArgumentException("Input array can not be null");
}
if (array.length < 2) {
return false;
}
for (int i = 0; i < array.length; i++) {
int pointer = convertToPositive(array[i]) - 1;
if (array[pointer] > 0) {
array[pointer] = changeSign(array[pointer]);
} else {
return true;
}
}
return false;
}
private static int convertToPositive(int value) {
return value < 0 ? changeSign(value) : value;
}
private static int changeSign(int value) {
return -1 * value;
}
}
I have coded it in vb.net and got 100/100 getting idea form solution by Guillermo
Private Function solution(A As Integer()) As Integer
' write your code in VB.NET 4.0
Dim Slice1() As Integer = Ending(A)
Dim slice2() As Integer = Starting(A)
Dim maxSUM As Integer = 0
For i As Integer = 1 To A.Length - 2
maxSUM = Math.Max(maxSUM, Slice1(i - 1) + slice2(i + 1))
Next
Return maxSUM
End Function
Public Shared Function Ending(input() As Integer) As Integer()
Dim result As Integer() = New Integer(input.Length - 1) {}
result(0) = InlineAssignHelper(result(input.Length - 1), 0)
For i As Integer = 1 To input.Length - 2
result(i) = Math.Max(0, result(i - 1) + input(i))
Next
Return result
End Function
Public Shared Function Starting(input() As Integer) As Integer()
Dim result As Integer() = New Integer(input.Length - 1) {}
result(0) = InlineAssignHelper(result(input.Length - 1), 0)
For i As Integer = input.Length - 2 To 1 Step -1
result(i) = Math.Max(0, result(i + 1) + input(i))
Next
Return result
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
Visit Codility to see the results
When I read solution to knapsack problem (http://en.wikipedia.org/wiki/Knapsack_problem), I couldn't understand why there is iteration number n in the argument. It seems we can come to leaf use case by checking the passed limit. Ex. the 15KG backpack problem, solution seems like:
Value(n, W){ // W = limit, n = # items still to choose from
if (n == 0) return 0;
if (arr[n][W] != unknown) return arr[n][W]; // <- add memoize
if (s[n] > W) result = Value(n-1,W);
else result = max{v[n] + Value(n-1, W-w[n]), Value(n-1, W)};
arr[n][W] = result; // <- add memoize
return result;
}
My non-memoize method looks like the below, which is easier to understand, at least for me, and also could be improved with memoization.
static int n =5;
static int [] w = new int[]{12,2,1,4,1}; //weight
static int [] v = new int[]{4,2,1,10,2}; //value
public static int knapSack(int wt){
int maxValue = 0,vtemp = 0, wtemp =0;
if (wt ==0) return 0;
for (int i=0; i<n; i++){
if (w[i] > wt) continue;
int tmp = v[i] + knapSack(wt - w[i]);
if (tmp > maxValue){
maxValue = tmp;
vtemp = v[i];
wtemp = w[i];
}
}
System.out.println("wt="+wt + ",vtemp="+vtemp+",wtemp="+wtemp+",ret max="+maxValue);
return maxValue;
}
So my question is:
why do we need n for argument?
statement if (s[n] > W) result = Value(n-1,W); make me even harder to understand why
I see the same big O for memoized version of my approach. Any other difference?
Thanks.
You're actually solving a different problem. The first piece of code (with n) solves the 0-1 knapsack problem, where you can choose to take at most one of any particular item (i.e. there is no "copying" of items). In that case, you need n to keep track of which items you've already used up.
In the second piece of code, you're solving the unbounded knapsack problem, in which you can take every item an unlimited number of times.
They're both forms of the NP-complete knapsack problem, but they have different solutions.
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;
}
}
}