I'm trying to solve the following problem:
I feel like I've given it a lot of thoughts and tried a lot of stuff. I manage to solve it, and produce correct values but the problem is that it isn't time efficient enough. It completes 2 out of the Kattis tests and fails on the 3 because of the time limit 1 second was exceeded. There is noway for me to see what the input was that they tested with I'm afraid.
I started out with a recursive solution and finished that. But then I realised that it wasn't time efficient enough so I instead tried to switch to an iterative solution.
I start with reading input and add those to an ArrayList. And then I call the following method with target as 1000.
public static int getCorrectWeight(List<Integer> platesArr, int target) {
/* Creates two lists, one for storing completed values after each iteration,
one for storing new values during iteration. */
List<Integer> vals = new ArrayList<>();
List<Integer> newVals = new ArrayList<>();
// Inserts 0 as a first value so that we can start the first iteration.
int best = 0;
vals.add(best);
for(int i=0; i < platesArr.size(); i++) {
for(int j=0; j < vals.size(); j++) {
int newVal = vals.get(j) + platesArr.get(i);
if (newVal <= target) {
newVals.add(newVal);
if (newVal > best) {
best = newVal;
}
} else if ((Math.abs(target-newVal) < Math.abs(target-best)) || (Math.abs(target-newVal) == Math.abs(target-best) && newVal > best)) {
best = newVal;
}
}
vals.addAll(newVals);
}
return best;
}
My question is, is there some way that I can reduce the time complexity on this one for large number of data?
The main problem is that the size of vals and newVals can grow very quickly, as each iteration can double their size. You only need to store 1000 or so values which should be manageable. You're limiting the values but because they're stored in an ArrayList, it ends up with a lot of duplicate values.
If instead, you used a HashSet, then it should help the efficiency a lot.
You only need to store a DP table of size 2001 (0 to 2000)
Let dp[i] represent if it is possible to form ikg of weights. If the weight goes over the array bounds, ignore it.
For example:
dp[0] = 1;
for (int i = 0; i < values.size(); i++){
for (int j = 2000; j >= values[i]; j--){
dp[j] = max(dp[j],dp[j-values[i]);
}
}
Here, values is where all the original weights are stored. All values of dp are to be set to 0 except for dp[0].
Then, check 1000 if it is possible to make it. If not, check 999 and 1001 and so on.
This should run in O(1000n + 2000) time, since n is at most 1000 this should run in time.
By the way, this is a modified knapsack algorithm, you might want to look up some other variants.
If you think too generally about this type of problem, you may think you have to check all possible combinations of input (each weight can be included or excluded), giving you 2n combinations to test if you have n inputs. This is, however, rather beside the point. Rather, the key here is that all weights are integers, and that the goal is 1000.
Let's examine corner cases first, because that limits the search space.
If all weights are >= 1000, pick the smallest.
If there is at least one weight < 1000, that is always better than any weight >= 2000, so you can ignore any weight >= 1000 for combination purposes.
Then, apply dynamic programming. Keep a set (you got HashSet as suggestion from other poster, but BitSet is even better since the maximum value in it is so small) of all combinations of the first k inputs, and increase k by combining all previous solutions with the k+1'th input.
When you have considered all possibilities, just search the bit vector for the best response.
static int count() {
int[] weights = new int[]{900, 500, 498, 4};
// Check for corner case to limit search later
int min = Integer.MAX_VALUE;
for (int weight : weights) min = Math.min(min, weight);
if (min >= 1000) {
return min;
}
// Get all interesting combinations
BitSet combos = new BitSet();
for (int weight : weights) {
if (weight < 1000) {
for (int t = combos.previousSetBit(2000 - weight) ; t >= 0; t = combos.previousSetBit(t-1)) {
combos.set(weight + t);
}
combos.set(weight);
}
}
// Pick best combo
for (int distance = 0; distance <= 1000; distance++) {
if (combos.get(1000 + distance)) {
return 1000 + distance;
}
if (combos.get(1000 - distance)) {
return 1000 - distance;
}
}
return 0;
}
Related
I have a method that returns a count of the objects that have a higher value than the average of all the objects within an ArrayList.
The 'User' object has the integer value stored within it's class (level).
My method works but I'm wondering if there's a better way of finding the average of all object values?
public int bestPlayers() {
ArrayList<User> players = new ArrayList<User>();
int bestPlayers = 0;
int totalPlayerLevel = 0;
double averageLevel = 0;
for (int i = 0; i < players.size(); i++) {
totalPlayerLevel += players.get(i).level;
}
averageLevel = totalPlayerLevel / players.size();
for (int i = 0; i < players.size(); i++) {
if (players.get(i).level > averageLevel) {
bestPlayers++;
}
}
return bestPlayers;
}
Java 8 provides a better way, using IntStream#average():
double average = players.stream()
.mapToInt(p -> p.level)
.average()
.orElse(0);
Then to output the number of "above average" players:
return (int) players.stream()
.filter(p -> p.level > average)
.count();
Your code works in O(n), since you travel through array twice. Well, since you have to calculate average, that means you have to travel at least once, so there is no option for performance faster then O(n).
Second thing you do, you must count players with level higher then average. Only speedup here could be if you have sorted array (have before, not calculating now, since its O(nlogn), then you can find first one with higher level then average and calculate number of the rest. That would cost O(logn), but its performance is still O(n), since you calculated average.
I've passed the sample input, and also some input which I decided to strike up (even the extreme cases), but I don't know why I got "Incorrect." Also, ignore the quickSort() call, I've implemented it and it works fine. In case you need an overview, here's the link to the problem: https://code.google.com/codejam/contest/2434486/dashboard#s=p0&a=0
I even checked the editorial and I don't know where I missed.
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for (int i = 0; i < t; i++) {
int size = in.nextInt();
int n = in.nextInt();
int ops = 0;
int[] sizes = new int[n];
for (int j = 0; j < n; j++) {
sizes[j] = in.nextInt();
}
quickSort(sizes);
for (int j = 0; j < n; j++) {
if (sizes[j] < size) {
size += sizes[j];
} else {
int newSize = (size * 2) - 1;
if (sizes[j] < newSize) {
size = newSize;
}
ops++;
}
}
System.out.println("Case #" + (i + 1) + ": " + ops);
}
So lets run through the logic of the problem you are trying to solve.
You have two options:
Remove a mote
Add a mote
Currently you are only adding motes, represented by newSize = (size * 2) - 1. Effectively doubling (almost) the size of your mote each time.
What if the new size of your mote is still insufficient to capture the mote represented by the current j iteration in your loop? You need to make the choice again of adding another mote, or removing that mote.
So lets say your mote has a size of 2 and the other motes have sizes of 1, 1, 3, 5, 8, 1000, 1001. You could either continue to add lots of motes until your mote can absorb 1000 and 1001, or you could just remove the two that are absurdly large.
With this revelation at seems that iteration will not always lead to the most optimal answer. At each step each choice you make is another possible path that could potentially hold the answer, but you want to find the decision path that leads to the least amount of operations. Its as if you want to follow all possible paths and then choose from the best solution found.
Luckily there are plenty of resources online about this. I recommend googling pathfinding and pathfinding algorithms. There is a bit to learn on the topic, but it's all interesting and fun.
Goodluck!
in addition to #br3nt answer lets consider this:
if i were to add a Simple Greedy solution to your algorithm it'll be a check that if
if (sizes[j] < size) {
// ...
else {
deleteMote(j);
}
whereas deleteMote(j) will do exactly as its name implies and delete the mote whenever more than one step(addition of a mote) is required to grow large enough to eat the mote at index J.
this may not be the perfect solution but that's the first step towards your goal.
Problem 10 from Project Euler:
The program runs for smaller numbers and slows to a crawl in the hundred thousands.
At 2 million, an answer fails to show up even though the program seems like it is still running.
I'm trying to implement the Sieve of Eratosthenes. It is supposed to be very fast. What's wrong with my approach?
import java.util.ArrayList;
public class p010
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 2000000;
for (int i = 2; i < upper; i++)
{
primes.add(i);
}
int sum = 0;
for (int i = 0; i < primes.size(); i++)
{
if (isPrime(primes.get(i)))
{
for (int k = 2; k*primes.get(i) < upper; k++)
{
if (primes.contains(k*primes.get(i)))
{
primes.remove(primes.indexOf(k*primes.get(i)));
}
}
}
}
for (int i = 0; i < primes.size(); i++)
{
sum += primes.get(i);
}
System.out.println(sum);
}
public static boolean isPrime(int number)
{
boolean returnVal = true;
for (int i = 2; i <= Math.sqrt(number); i ++)
{
if (number % i == 0)
{
returnVal = false;
}
}
return returnVal;
}
}
You appear to be trying to implement the Sieve of Eratosthenes which should perform better that O(N^2) (In fact, Wikipedia says it is O(N log(log N)) ...).
The fundamental problem is your choice of data structure. You've chosen to represent the set of remaining prime candidates as an ArrayList of primes. This means that your test to see if a number is still in the set takes O(N) comparisons ... where N is the number of remaining primes. Then you are using ArrayList.remove(int) to remove the non-primes ... which is O(N) also.
That all adds up to making your Sieve implementation worse than O(N^2).
The solution is to replace the ArrayList<Integer> with an boolean[] where the positions (indexes) in the boolean array represent the numbers, and the value of the boolean says whether the number is prime / possibly prime, or not prime.
(There were other problems too that I didn't notice ... see the other answers.)
There are a few issues here. First, lets talk about the algorithm. Your isPrime method is actually the very thing that the sieve is designed to avoid. When you get to a number in the sieve, you already know it's prime, you don't need to test it. If it weren't prime, it would already have been eliminated as a factor of a lower number.
So, point 1:
You can eliminate the isPrime method altogether. It should never return false.
Then, there are implementation issues. primes.contains and primes.remove are problems. They run in linear time on an ArrayList, because they require checking each element or rewriting a large portion of the backing array.
Point 2:
Either mark values in place (use boolean[], or use some other more appropriate data structure.)
I typically use something like boolean primes = new boolean[upper+1], and define n to be included if !(primes[n]). (I just ignore elements 0 and 1 so I don't have to subtract indices.) To "remove" an element, I set it to true. You could also use something like TreeSet<Integer>, I suppose. Using boolean[], the method is near-instantaneous.
Point 3:
sum needs to be a long. The answer (roughly 1.429e11) is larger than the maximum value of an integer (2^31-1)
I can post working code if you like, but here's a test output, without spoilers:
public static void main(String[] args) {
long value;
long start;
long finish;
start = System.nanoTime();
value = arrayMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
start = System.nanoTime();
value = treeMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
}
output:
Using boolean[]
Value: 1.429e+11, time: 17 ms
Using TreeSet<Integer>
Value: 1.429e+11, time: 4869 ms
Edit:
Since spoilers are posted, here's my code:
public static long arrayMethod(int upper) {
boolean[] primes = new boolean[upper+1];
long sum = 0;
for (int i = 2; i <=upper; i++) {
if (!primes[i]) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes[k] = true;
}
}
}
return sum;
}
public static long treeMethod(int upper) {
TreeSet<Integer> primes = new TreeSet<Integer>();
for (int i = 2; i <= upper; i++) {
primes.add(i);
}
long sum = 0;
for (Integer i = 2; i != null; i=primes.higher(i)) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes.remove(k);
}
}
return sum;
}
Two things:
Your code is hard to follow. You have a list called "primes", that contains non prime numbers!
Also, you should strongly consider whether or not an array list is appropriate. In this case, a LinkedList would be much more efficient.
Why is this? An array list must constantly resize an array by: asking for new memory to create an array, then copying the old memory over in the newly created array. A Linked list would just resize the memory by changing a pointer. This is a lot quicker! However, I do not think that by making this change you can salvage your algorithm.
You should use an array list if you need to access the items non-sequentially, here, (with a suitable algorithm) you need to access the items sequentially.
Also, your algorithm is slow.Take the advice of SJuan76 (or gyrogearless), thanks sjuan76
The key to the efficiency of classic implementation of the sieve of Eratosthenes on modern CPUs is the direct (i.e. non-sequential) memory access. Fortunately, ArrayList<E> does implement RandomAccess.
Another key to the sieve's efficiency is its conflation of index and value, just like in integer sorting. Actually removing any number from the sequence destroys this ability to directly address without any computations. We must mark, not remove, any composite as we find them, so any numbers greater than it will remain in their places in the sequence.
ArrayList<Integer> can be used for that (except taking more memory than is strictly necessary, but for 2 million this is inconsequential).
So your code with a minimal edit fix (also changing sum to be long as others point out too), becomes
import java.util.ArrayList;
public class Main
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 5000;
primes.ensureCapacity(upper);
for (int i = 0; i < upper; i++) {
primes.add(i);
}
long sum = 0;
for (int i = 2; i <= upper / i; i++) {
if ( primes.get(i) > 0 ) {
for (int k = i*i; k < upper ; k+=i) {
primes.set(k, 0);
}
}
}
for (int i = 2; i < upper; i++) {
sum += primes.get(i);
}
System.out.println(sum);
}
}
Finds the result for 2000000 in half a second on Ideone. The projected run time for your original code there: between 10 and 400 hours (!).
To find rough estimates for the run time when faced with a slow code, you should always try to find out its empirical orders of growth: run it for some small size n1, then a bigger size n2, record the run times t1 and t2. If t ~ n^a, then a = log(t2/t1) / log(n2/n1).
For your original code the empirical orders of growth measured on 10k .. 20k .. 40k range of upper limit value N, are ~ N^1.7 .. N^1.9 .. N^2.1. For the fixed code it's faster than ~ N (in fact, it's ~ N^0.9 in the tested range 0.5 mln .. 1 mln .. 2 mln). The theoretical complexity is O(N log (log N)).
Your program is not the Sieve of Eratosthenes; the modulo operator gives it away. Your program will be O(n^2), where a proper Sieve of Eratosthenes is O(n log log n), which is essentially n. Here's my version; I'll leave it to you to translate to Java with appropriate numeric datatypes:
function sumPrimes(n)
sum := 0
sieve := makeArray(2..n, True)
for p from 2 to n step 1
if sieve[p]
sum := sum + p
for i from p * p to n step p
sieve[i] := False
return sum
If you're interested in programming with prime numbers, I modestly recommend this essay at my blog.
The problem in question can be found at http://projecteuler.net/problem=14
I'm trying what I think is a novel solution. At least it is not brute-force. My solution works on two assumptions:
1) The less times you have iterate through the sequence, the quicker you'll get the answer. 2) A sequence will necessarily be longer than the sequences of each of its elements
So I implemented an array of all possible numbers that could appear in the sequence. The highest number starting a sequence is 999999 (as the problem only asks you to test numbers less than 1,000,000); therefore the highest possible number in any sequence is 3 * 999999 + 1 = 2999998 (which is even, so would then be divided by 2 for the next number in the sequence). So the array need only be of this size. (In my code the array is actually 2999999 elements, as I have included 0 so that each number matches its array index. However, this isn't necessary, it is for comprehension).
So once a number comes in a sequence, its value in the array becomes 0. If subsequent sequences reach this value, they will know not to proceed any further, as it is assumed they will be longer.
However, when i run the code I get the following error, at the line introducing the "wh:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3188644
For some reason it is trying to access an index of the above value, which shouldn't be reachable as it is over the possible max of 29999999. Can anyone understand why this is happening?
Please note that I have no idea if my assumptions are actually sound. I'm an amateur programmer and not a mathematician. I'm experimenting. Hopefully I'll find out whether it works as soon as I get the indexing correct.
Code is as follows:
private static final int MAX_START = 999999;
private static final int MAX_POSSIBLE = 3 * MAX_START + 1;
public long calculate()
{
int[] numbers = new int[MAX_POSSIBLE + 1];
for(int index = 0; index <= MAX_POSSIBLE; index++)
{
numbers[index] = index;
}
int longestChainStart = 0;
for(int index = 1; index <= numbers.length; index++)
{
int currentValue = index;
if(numbers[currentValue] != 0)
{
longestChainStart = currentValue;
while(numbers[currentValue] != 0 && currentValue != 1)
{
numbers[currentValue] = 0;
if(currentValue % 2 == 0)
{
currentValue /= 2;
}
else
{
currentValue = 3 * currentValue + 1;
}
}
}
}
return longestChainStart;
}
Given that you can't (easily) put a limit on the possible maximum number of a sequence, you might want to try a different approach. I might suggest something based on memoization.
Suppose you've got an array of size 1,000,000. Each entry i will represent the length of the sequence from i to 1. Remember, you don't need the sequences themselves, but rather, only the length of the sequences. You can start filling in your table at 1---the length is 0. Starting at 2, you've got length 1, and so on. Now, say we're looking at entry n, which is even. You can look at the length of the sequence at entry n/2 and just add 1 to that for the value at n. If you haven't calculated n/2 yet, just do the normal calculations until you get to a value you have calculated. A similar process holds if n is odd.
This should bring your algorithm's running time down significantly, and prevent any problems with out-of-bounds errors.
You can solve this by this way
import java.util.LinkedList;
public class Problem14 {
public static void main(String[] args) {
LinkedList<Long> list = new LinkedList<Long>();
long length =0;
int res =0;
for(int j=10; j<1000000; j++)
{
long i=j;
while(i!=1)
{
if(i%2==0)
{
i =i/2;
list.add(i);
}
else
{
i =3*i+1;
list.add(i);
}
}
if(list.size()>length)
{
length =list.size();
res=j;
}
list.clear();
}
System.out.println(res+ " highest nuber and its length " + length);
}}
I essentially have a bunch of data objects which map timestamps in milliseconds to float values. I'm looking to essentially find the peak/max of the data in a given range. I've been essentially using something like this:
float previousValue = 0;
for (int i = 0; i < data.size(); i++) {
MyData value = data.get(i);
if (value.getData() < previousValue) {
// found the peak!
break;
} else {
previousValue = value.getData();
}
}
The only problem with this algorithm is that it doesn't account for noise. Essentially, I could have values like this:
[0.1025, 0.3000, 0.3025, 0.3500, 0.3475, 0.3525, 0.1025]
The actual peak is at 0.3525, but my algorithm above would see it as 0.3500, as it comes first. Due to the nature of my calculations, I can't just do max() on the array and find out the largest value, I need to find the largest value that comes first before it falls.
How can I find the top of my peak, while accounting for some variance in noise?
There are two issues:
filtering out the noise;
finding the peak.
It seems like you already have a solution for 2, and need to solve 1.
To filter out the noise, you need some kind of low-pass filter. A moving average is one such filter. For example, exponential moving average is very easy to implement and should work well.
In summary: put your series through the filter, and then apply the peak finding algorithm.
an easier method to find a single peak (or the highest value) in an array (any numeric array: int, double) is to loop through the array and set a variable to the highest value...
Example: (all examples use a float array called "data")
float highest = 0; //use a number equal to or below the lowest possible value
for (int i = 0; i < data.length; i++){
if (data[i] > highest){
highest = data[i];
}
}
to find multiple peaks in noisy data filtering some of the noise out I used this method:
boolean[] isPeak = new boolean[20]; // I am looking for 20 highest peaks
float[] filter = new float[9]; // the range to which I want to define a peak is 9
float[] peaks = new float[20]; // again the 20 peaks I want to find
float lowpeak = 100; // use a value higher than the highest possible value
// first we start the filter cycling through the data
for (int i = 0; i < data.length; i++){
for (int a = filter.length-1; a > 0; a--){
filter[a] = filter[a-1];
}
filter[0] = data[1]
// now we check to see if the filter detects a peak
if (filter[4]>filter[0] && filter[4]>filter[1] && filter[4]>filter[2] &&
filter[4]>filter[3] && filter[4]>filter[5] && filter[4]>filter[6] &&
filter[4]>filter[7] && filter[4]>filter[8]){
// now we find the lowest peak
for (int x = 0; x < peaks.lengt-1; x++){
if (peaks[x] < lowpeak){
lowpeak = peaks[x];
}
}
// now we check to see if the peak is above the lowest peak
for (int x = 0; x < peaks.length; x++){
if (peaks[x] > lowpeak && peaks[x] != peaks[x+1]){
for (int y = peaks.length-1; y > 0 && !isPeak[y]; y--){
peaks[y] = peaks[y-1];
}
peaks[0] = filter[4];
}
}
}
}
this may not be the most efficient way to do this but it gets the job done!