Related
I'm solving Codility questions as practice and couldn't answer one of the questions. I found the answer on the Internet but I don't get how this algorithm works. Could someone walk me through it step-by-step?
Here is the question:
/*
You are given integers K, M and a non-empty zero-indexed array A consisting of N integers.
Every element of the array is not greater than M.
You should divide this array into K blocks of consecutive elements.
The size of the block is any integer between 0 and N. Every element of the array should belong to some block.
The sum of the block from X to Y equals A[X] + A[X + 1] + ... + A[Y]. The sum of empty block equals 0.
The large sum is the maximal sum of any block.
For example, you are given integers K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
The array can be divided, for example, into the following blocks:
[2, 1, 5, 1, 2, 2, 2], [], [] with a large sum of 15;
[2], [1, 5, 1, 2], [2, 2] with a large sum of 9;
[2, 1, 5], [], [1, 2, 2, 2] with a large sum of 8;
[2, 1], [5, 1], [2, 2, 2] with a large sum of 6.
The goal is to minimize the large sum. In the above example, 6 is the minimal large sum.
Write a function:
class Solution { public int solution(int K, int M, int[] A); }
that, given integers K, M and a non-empty zero-indexed array A consisting of N integers, returns the minimal large sum.
For example, given K = 3, M = 5 and array A such that:
A[0] = 2
A[1] = 1
A[2] = 5
A[3] = 1
A[4] = 2
A[5] = 2
A[6] = 2
the function should return 6, as explained above. Assume that:
N and K are integers within the range [1..100,000];
M is an integer within the range [0..10,000];
each element of array A is an integer within the range [0..M].
Complexity:
expected worst-case time complexity is O(N*log(N+M));
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
*/
And here is the solution I found with my comments about parts which I don't understand:
public static int solution(int K, int M, int[] A) {
int lower = max(A); // why lower is max?
int upper = sum(A); // why upper is sum?
while (true) {
int mid = (lower + upper) / 2;
int blocks = calculateBlockCount(A, mid); // don't I have specified number of blocks? What blocks do? Don't get that.
if (blocks < K) {
upper = mid - 1;
} else if (blocks > K) {
lower = mid + 1;
} else {
return upper;
}
}
}
private static int calculateBlockCount(int[] array, int maxSum) {
int count = 0;
int sum = array[0];
for (int i = 1; i < array.length; i++) {
if (sum + array[i] > maxSum) {
count++;
sum = array[i];
} else {
sum += array[i];
}
}
return count;
}
// returns sum of all elements in an array
private static int sum(int[] input) {
int sum = 0;
for (int n : input) {
sum += n;
}
return sum;
}
// returns max value in an array
private static int max(int[] input) {
int max = -1;
for (int n : input) {
if (n > max) {
max = n;
}
}
return max;
}
So what the code does is using a form of binary search (How binary search works is explained quite nicely here, https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/. It also uses an example quite similar to your problem.). Where you search for the minimum sum every block needs to contain. In the example case, you need the divide the array in 3 parts
When doing a binary search you need to define 2 boundaries, where you are certain that your answer can be found in between. Here, the lower boundary is the maximum value in the array (lower). For the example, this is 5 (this is if you divide your array in 7 blocks). The upper boundary (upper) is 15, which is the sum of all the elements in the array (this is if you divide the array in 1 block.)
Now comes the search part: In solution() you start with your bounds and mid point (10 for the example).
In calculateBlockCount you count (count ++ does that) how many blocks you can make if your sum is a maximum of 10 (your middle point/ or maxSum in calculateBlockCount).
For the example 10 (in the while loop) this is 2 blocks, now the code returns this (blocks) to solution. Then it checks whether is less or more than K, which is the number of blocks you want. If its less than K your mid point is high because you're putting to many array elements in your blocks. If it's more than K, than your mid point is too high and you're putting too little array elements in your array.
Now after the checking this, it halves the solution space (upper = mid-1).
This happens every loop, it halves the solution space which makes it converge quite quickly.
Now you keep going through your while adjusting the mid, till this gives the amount blocks which was in your input K.
So to go though it step by step:
Mid =10 , calculateBlockCount returns 2 blocks
solution. 2 blocks < K so upper -> mid-1 =9, mid -> 7 (lower is 5)
Mid =7 , calculateBlockCount returns 2 blocks
solution() 2 blocks < K so upper -> mid-1 =6, mid -> 5 (lower is 5, cast to int makes it 5)
Mid =5 , calculateBlockCount returns 4 blocks
solution() 4 blocks < K so lower -> mid+1 =6, mid -> 6 (lower is 6, upper is 6
Mid =6 , calculateBlockCount returns 3 blocks
So the function returns mid =6....
Hope this helps,
Gl learning to code :)
Edit. When using binary search a prerequisite is that the solution space is a monotonic function. This is true in this case as when K increases the sum is strictly decreasing.
Seems like your solution has some problems. I rewrote it as below:
class Solution {
public int solution(int K, int M, int[] A) {
// write your code in Java SE 8
int high = sum(A);
int low = max(A);
int mid = 0;
int smallestSum = 0;
while (high >= low) {
mid = (high + low) / 2;
int numberOfBlock = blockCount(mid, A);
if (numberOfBlock > K) {
low = mid + 1;
} else if (numberOfBlock <= K) {
smallestSum = mid;
high = mid - 1;
}
}
return smallestSum;
}
public int sum(int[] A) {
int total = 0;
for (int i = 0; i < A.length; i++) {
total += A[i];
}
return total;
}
public int max(int[] A) {
int max = 0;
for (int i = 0; i < A.length; i++) {
if (max < A[i]) max = A[i];
}
return max;
}
public int blockCount(int max, int[] A) {
int current = 0;
int count = 1;
for (int i = 0; i< A.length; i++) {
if (current + A[i] > max) {
current = A[i];
count++;
} else {
current += A[i];
}
}
return count;
}
}
This is helped me in case anyone else finds it helpful.
Think of it as a function: given k (the block count) we get some largeSum.
What is the inverse of this function? It's that given largeSum we get a k. This inverse function is implemented below.
In solution() we keep plugging guesses for largeSum into the inverse function until it returns the k given in the exercise.
To speed up the guessing process, we use binary search.
public class Problem {
int SLICE_MAX = 100 * 1000 + 1;
public int solution(int blockCount, int maxElement, int[] array) {
// maxGuess is determined by looking at what the max possible largeSum could be
// this happens if all elements are m and the blockCount is 1
// Math.max is necessary, because blockCount can exceed array.length,
// but this shouldn't lower maxGuess
int maxGuess = (Math.max(array.length / blockCount, array.length)) * maxElement;
int minGuess = 0;
return helper(blockCount, array, minGuess, maxGuess);
}
private int helper(int targetBlockCount, int[] array, int minGuess, int maxGuess) {
int guess = minGuess + (maxGuess - minGuess) / 2;
int resultBlockCount = inverseFunction(array, guess);
// if resultBlockCount == targetBlockCount this is not necessarily the solution
// as there might be a lower largeSum, which also satisfies resultBlockCount == targetBlockCount
if (resultBlockCount <= targetBlockCount) {
if (minGuess == guess) return guess;
// even if resultBlockCount == targetBlockCount
// we keep searching for potential lower largeSum that also satisfies resultBlockCount == targetBlockCount
// note that the search range below includes 'guess', as this might in fact be the lowest possible solution
// but we need to check in case there's a lower one
return helper(targetBlockCount, array, minGuess, guess);
} else {
return helper(targetBlockCount, array, guess + 1, maxGuess);
}
}
// think of it as a function: given k (blockCount) we get some largeSum
// the inverse of the above function is that given largeSum we get a k
// in solution() we will keep guessing largeSum using binary search until
// we hit k given in the exercise
int inverseFunction(int[] array, int largeSumGuess) {
int runningSum = 0;
int blockCount = 1;
for (int i = 0; i < array.length; i++) {
int current = array[i];
if (current > largeSumGuess) return SLICE_MAX;
if (runningSum + current <= largeSumGuess) {
runningSum += current;
} else {
runningSum = current;
blockCount++;
}
}
return blockCount;
}
}
From anhtuannd's code, I refactored using Java 8. It is slightly slower. Thanks anhtuannd.
IntSummaryStatistics summary = Arrays.stream(A).summaryStatistics();
long high = summary.getSum();
long low = summary.getMax();
long result = 0;
while (high >= low) {
long mid = (high + low) / 2;
AtomicLong blocks = new AtomicLong(1);
Arrays.stream(A).reduce(0, (acc, val) -> {
if (acc + val > mid) {
blocks.incrementAndGet();
return val;
} else {
return acc + val;
}
});
if (blocks.get() > K) {
low = mid + 1;
} else if (blocks.get() <= K) {
result = mid;
high = mid - 1;
}
}
return (int) result;
I wrote a 100% solution in python here. The result is here.
Remember: You are searching the set of possible answers not the array A
In the example given they are searching for possible answers. Consider [5] as 5 being the smallest max value for a block. And consider [2, 1, 5, 1, 2, 2, 2] 15 as the largest max value for a block.
Mid = (5 + 15) // 2. Slicing out blocks of 10 at a time won't create more than 3 blocks in total.
Make 10-1 the upper and try again (5+9)//2 is 7. Slicing out blocks of 7 at a time won't create more than 3 blocks in total.
Make 7-1 the upper and try again (5+6)//2 is 5. Slicing out blocks of 5 at a time will create more than 3 blocks in total.
Make 5+1 the lower and try again (6+6)//2 is 6. Slicing out blocks of 6 at a time won't create more than 3 blocks in total.
Therefore 6 is the lowest limit to impose on the sum of a block that will permit breaking into 3 blocks.
Its an assignment task,I have spend 2 days to come up with a solution but still having lots of confusion,however here I need to make few points clear. Following is the problem:
Yuckdonald’s is considering opening a series of restaurant along QVH. n possible locations are along a straight line and the distances of these locations from the start of QVH are in miles and in increasing order m1, m2, ...., mn. The constraints are as follows:
1. At each location, Yuckdonald may open one restaurant and expected profit from opening a restaurant at location i is given as pi
2. Any two restaurants should be at least k miles apart, where k is a positive integer
My solution:
public class RestaurantProblem {
int[] Profit;
int[] P;
int[] L;
int k;
public RestaurantProblem(int[] L , int[] P, int k) {
this.L = L;
this.P = P;
this.k = k;
Profit = new int[L.length];
}
public int compute(int i){
if(i==0)
return 0;
Profit[i]= P[i]+(L[i]-L[i-1]< k ? 0:compute(i-1));//if condition satisfies then adding previous otherwise zero
if (Profit[i]<compute(i-1)){
Profit[i] = compute(i-1);
}
return Profit[i];
}
public static void main(String args[]){
int[] m = {0,5,10,15,19,25,28,29};
int[] p = {0,10,4,61,21,13,19,15};
int k = 5;
RestaurantProblem rp = new RestaurantProblem(m, p ,k);
rp.compute(m.length-1);
for(int n : rp.Profit)
System.out.println(n);
}
}
This solution giving me 88 however if I exclude (Restaurant at 25 with Profit 13) and include (Restaurant 28 with profit 19) I can have 94 max...
point me if I am wrong or how can I achieve this if its true.
I was able to identify 2 mistakes:
You are not actually using dynamic programming
, you are just storing the results in a data structure, which wouldn't be that bad for performance if the program worked the way you have written it and if you did only 1 recursive call.
However you do at least 2 recursive calls. Therefore the program runs in Ω(2^n) instead of O(n).
Dynamic programming usually works like this (pseudocode):
calculate(input) {
if (value already calculated for input)
return previously calculated value
else
calculate and store value for input and return result
}
You could do this by initializing the array elements to -1 (or 0 if all profits are positive):
Profit = new int[L.length];
Arrays.fill(Profit, -1); // no need to do this, if you are using 0
public int compute(int i) {
if (Profit[i] >= 0) { // modify the check, if you're using 0 for non-calculated values
// reuse already calculated value
return Profit[i];
}
...
You assume the previous restaurant can only be build at the previous position
Profit[i] = P[i] + (L[i]-L[i-1]< k ? 0 : compute(i-1));
^
Just ignores all positions before i-1
Instead you should use the profit for the last position that is at least k miles away.
Example
k = 3
L 1 2 3 ... 100
P 5 5 5 ... 5
here L[i] - L[i-1] < k is true for all i and therefore the result will just be P[99] = 5 but it should be 34 * 5 = 170.
int[] lastPos;
public RestaurantProblem(int[] L, int[] P, int k) {
this.L = L;
this.P = P;
this.k = k;
Profit = new int[L.length];
lastPos = new int[L.length];
Arrays.fill(lastPos, -2);
Arrays.fill(Profit, -1);
}
public int computeLastPos(int i) {
if (i < 0) {
return -1;
}
if (lastPos[i] >= -1) {
return lastPos[i];
}
int max = L[i] - k;
int lastLastPos = computeLastPos(i - 1), temp;
while ((temp = lastLastPos + 1) < i && L[temp] <= max) {
lastLastPos++;
}
return lastPos[i] = lastLastPos;
}
public int compute(int i) {
if (i < 0) {
// no restaurants can be build before pos 0
return 0;
}
if (Profit[i] >= 0) { // modify the check, if you're using 0 for non-calculated values
// reuse already calculated value
return Profit[i];
}
int profitNoRestaurant = compute(i - 1);
if (P[i] <= 0) {
// no profit can be gained by building this restaurant
return Profit[i] = profitNoRestaurant;
}
return Profit[i] = Math.max(profitNoRestaurant, P[i] + compute(computeLastPos(i)));
}
To my understanding, the prolem can be modelled with a two-dimensional state space, which I don't find in the presented implementation. For each (i,j) in{0,...,n-1}times{0,...,n-1}` let
profit(i,j) := the maximum profit attainable for selecting locations
from {0,...,i} where the farthest location selected is
no further than at position j
(or minus infinity if no such solution exist)
and note that the recurrence relation
profit(i,j) = min{ p[i] + profit(i-1,lastpos(i)),
profit(i-1,j)
}
where lastpos(i) is the location which is farthest from the start, but no closer than k to position i; the first case above corresponds to selection location i into the solution while the second case corresponds to omitting location j in the solution. The overall solution can be obtained by evaluating profit(n-1,n-1); the evaluation can be done either recursively or by filling a two-dimensional array in a bottom-up manner and returning its contents at (n-1,n-1).
Given an array of integers, which can contain both +ve and -ve numbers. I've to maximize the product of any 3 elements of the array. The elements can be non-contiguous.
Some examples:
int[] arr = {-5, -7, 4, 2, 1, 9}; // Max Product of 3 numbers = -5 * -7 * 9
int[] arr2 = {4, 5, -19, 3}; // Max Product of 3 numbers = 4 * 5 * 3
I've tried solving it using Dynamic Programming, but I'm not getting the expected result. It is returning the result often involving the same number twice in the multiplication. So, for the array - {4, 2, 1, 9}, it is returning - 32, which is 4 * 4 * 2.
Here's my code:
public static int maxProduct(int[] arr, int count) {
return maxProduct(arr, 0, arr.length - 1, count);
}
private static int maxProduct(int[] arr, int fromIndex, int toIndex, int count) {
if (count == 1) {
return maximum(arr, fromIndex, toIndex);
} else if (toIndex - fromIndex + 1 < count) {
return 1;
} else {
return MathUtil.max(maxProduct(arr, fromIndex, toIndex - 1, count - 1) * arr[toIndex - 1],
maxProduct(arr, fromIndex, toIndex - 1, count));
}
}
MathUtil.max(int a, int b) is a method that gives maximum of a and b.
The two values I pass to max method there are:
maxProduct, when we consider last element as a part of product.
maxProduct, when we don't consider it as a part of product.
count contains the number of element we want to consider. Here 3.
For count == 1, we have to find maximum of 1 element from array. That means, we have to use maximum array element.
If toIndex - fromIndex + 1 < count, means, there are not enough elements in the array between those indices.
I've an intuition that, the first if condition is one of the reason of failure. Because, it is only considering maximum element from an array, while the maximum product may comprise of negative numbers too. But I don't know how to take care of that.
The reason I'm using Dynamic Programming is that I can then generalize this solution to work for any value of count. Of course, if someone have any better approach, even for count = 3, I welcome the suggestion (I would want to avoid sorting the array, as that will be another O(nlogn) at the least).
Sort the given array in ascending order and you have to take the maximum of these cases
to get the answer..
product of last 3 numbers in sorted array
Product of first two and last number in the sorted array
For count=3, your solution will have 1 of 3 forms:
The 3 largest positive values (assuming there ARE 3 positive values)
The largest positive value and the 2 smallest negative values (assuming there IS a positive value)
The 3 least negative values
Each of which can be solved a lot easier than using DP.
It is always max of(smallest two negative digits and biggest positive or
last three big positive numbers)
public static void main(String args[]){
int array[] = {-5,-1,4,2,1,9};
Arrays.sort(array);
int length = array.length;
System.out.println(max(array[0]*array[1]*array[length-1],
array[length-1]*array[length-2]*array[length-3]));
}
Sort The Array
Then max will be either the product of last 3 or first 2(if negative) and the last.
Arrays.sort(arr);
int max1 = (arr[n - 1] * arr[n - 2] * arr[n - 3]);
int max2 = (arr[0] * arr[1] * arr[n - 1]);
System.out.println(max1 > max2 ? max1 : max2);
n=len(arr1)
for i in range(0,n):
arr1[i]=abs(arr1[i])
arr1.sort()
return arr1[n-1]*arr1[n-2]*arr1[n-3]
even though this solution is simple this basically involves sorting the array and then taking the product of last three numbers , before that is to be done ; all the values in the array should be positive .which is done by the first for loop.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ComputeMaxProduct {
public static void main(String[] args){
int [] arr = {4, 5, -19, 3};
List<Integer> superSet = new ArrayList<>();
for (int a : arr ){
superSet.add(a);
}
int k = 3;
int maxProduct = computeMaxProduct(superSet, k);
System.out.println("maximum product is : " + maxProduct);
}
private static int computeMaxProduct( List<Integer> superSet, int k ){
List<Set<Integer>> res = getSubsets(superSet,k);
int maxProduct = 1;
for(int index = 0; index < res.size(); index++){
int product = 1;
for(Integer i : res.get(index)){
product *= i;
}
if (product > maxProduct){
maxProduct = product;
}
}
return maxProduct;
}
private static void getSubsets(List<Integer> superSet, int k, int idx, Set<Integer> current,List<Set<Integer>> solution) {
//successful stop clause
if (current.size() == k) {
solution.add(new HashSet<>(current));
return;
}
//unseccessful stop clause
if (idx == superSet.size()) return;
Integer x = superSet.get(idx);
current.add(x);
//"guess" x is in the subset
getSubsets(superSet, k, idx+1, current, solution);
current.remove(x);
//"guess" x is not in the subset
getSubsets(superSet, k, idx+1, current, solution);
}
public static List<Set<Integer>> getSubsets(List<Integer> superSet, int k) {
List<Set<Integer>> res = new ArrayList<>();
getSubsets(superSet, k, 0, new HashSet<Integer>(), res);
return res;
}
}
public class MaxProdofThreenumbers {
public int ThreeLargeNumbers(int[] a) {
int topfirstpos = 0;
int topsecpos = 0;
int topthirdpos = 0;
int topfirstneg = 0;
int topsecneg = 0;
int prodneg = 0;
int prodpos = 0;
int prodmax = 0;
boolean flag = false;
for (int i = 0; i < a.length; i++) {
String num = a[i] + "";
if (num.contains("-")) {
String array[] = num.split("-");
num = array[1];
flag = true;
} else
flag = false;
if (flag) {
if (topfirstneg < Integer.valueOf(num)) {
topsecneg = topfirstneg;
topfirstneg = Integer.valueOf(num);
} else if (topsecneg < Integer.valueOf(num)) {
topsecneg = Integer.valueOf(num);
}
}
else {
if (topfirstpos < Integer.valueOf(num)) {
topsecpos = topfirstpos;
topfirstpos = Integer.valueOf(num);
}
else if (topsecpos < Integer.valueOf(num)) {
topthirdpos = topsecpos;
topsecpos = Integer.valueOf(num);
}
else if (topthirdpos < Integer.valueOf(num)) {
topthirdpos = Integer.valueOf(num);
}
}
}
prodneg = topfirstneg * topsecneg;
prodpos = topfirstpos * topsecpos;
if (prodneg > prodpos) {
prodmax = prodneg * topfirstpos;
} else {
prodmax = prodpos * topthirdpos;
}
return prodmax;
}
public static void main(String a[]) {
int list[] = { -29, 3, -2, -57, 8, -789, 34 };
MaxProdofThreenumbers t = new MaxProdofThreenumbers();
System.out.println(t.ThreeLargeNumbers(list));
}
}
This problem can be done in O(n) time.
Keep track of these 5 variables and update them during every iteration:
highest product of 3 numbers
highest product of 2 numbers
highest element
lowest product of 2 numbers
lowest element
After last iteration, product of 3 numbers variable will be the answer.
package interviewProblems;
import interviewProblems.exceptions.ArrayTooSmallException;
import java.util.PriorityQueue;
public class Problem5 {
public static void main(String[] args) {
int[] data1 = new int[]{}; // error
int[] data2 = new int[]{1, 5}; // error
int[] data3 = new int[]{1, 4, 2, 8, 9}; // Case: all positive --> 3-max
int[] data4 = new int[]{10, 11, 12, -20}; // Case: 1 negative --> 3-max
int[] data5 = new int[]{-5, -6, -10, 7, 8, 9}; // Case: 2+ negative --> 3-max || 1-max 2-small
int[] data6 = new int[]{-12, -10, -6, -4}; // Case: all negative --> 3-max
int[] data7 = new int[]{-10, -10, 1, 3, 2};
try {
productOfThree(data2);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
System.out.println(productOfThree(data3));
System.out.println(productOfThree(data4));
System.out.println(productOfThree(data5));
System.out.println(productOfThree(data6));
System.out.println(productOfThree(data7));
} catch (Exception e) {
System.out.println("You should not see this line");
}
}
// O(n) time
// O(1) memory
private static int productOfThree(int[] data) throws ArrayTooSmallException {
if (data.length < 3) {
throw new ArrayTooSmallException(3 , data.length);
}
PriorityQueue<Integer> maxNumbers = new PriorityQueue<>(); // keep track of 3 largest numbers
PriorityQueue<Integer> minNumbers = new PriorityQueue<>((x, y) -> y - x); // keep track of two smallest numbers
for (int i = 0; i < data.length; i++) {
maxNumbers.add(data[i]);
minNumbers.add(data[i]);
if(maxNumbers.size() > 3) {
maxNumbers.poll();
}
if(minNumbers.size() > 2){
minNumbers.poll();
}
}
int maxLow = maxNumbers.poll();
int maxMed = maxNumbers.poll();
int maxHigh = maxNumbers.poll();
int minHigh = minNumbers.poll();
int minLow = minNumbers.poll();
int possibleProduct1 = maxHigh * maxMed * maxLow;
int possibleProduct2 = maxHigh * minHigh * minLow;
return Math.max(possibleProduct1, possibleProduct2);
}
// O(n) time
// O(n) memory
// private static int productOfThree(int[] data) throws ArrayTooSmallException {
// if(data.length < 3) {
// throw new ArrayTooSmallException("Array must be at least 3 long to preform productOfThree(int[] data)");
// }
//
// PriorityQueue<Integer> maxNumbers = new PriorityQueue<>((x , y) -> y - x); // keep track of 3 largest numbers
// PriorityQueue<Integer> minNumbers = new PriorityQueue<>(); // keep track of two smallest numbers
//
// for(int i = 0; i < data.length; i++) {
// maxNumbers.add(data[i]);
// minNumbers.add(data[i]);
// }
//
// int maxHigh = maxNumbers.poll();
// int maxMed = maxNumbers.poll();
// int maxLow = maxNumbers.poll();
//
// int minLow = minNumbers.poll();
// int minHigh = minNumbers.poll();
//
// int possibleProduct1 = maxHigh * maxMed * maxLow;
// int possibleProduct2 = maxHigh * minHigh * minLow;
//
// return Math.max(possibleProduct1 , possibleProduct2);
// }
}
https://github.com/amilner42/interviewPractice/blob/master/src/interviewProblems/Problem5.java
Assuming that the a positive product is bigger than a negative product, I can think of the following way it can be done.
If there are less than two negative elements in the array, then it is simple, product of top 3(top == positive) elements.
If negative numbers are chosen, at least 2 of them have to be in the product, so that product is positive. Therefore whatever be the case, the top (positive) number will always be part of the product.
Multiply last two(negatives) and 2nd and 3rd highest(positives) and compare. Out of these two pairs whichever has higher value, will be part of the final product along with the top positive shortlisted in line above.
https://stackoverflow.com/users/2466168/maandoo 's answer is the best.
As, he said, answer is max(l,r) for
r. product of last 3 numbers in sorted array
l. product of first two and last number in the sorted array
Let me elaborate now.
I think this problem is confusion because each number can be positive, negative and zero. 3 state is annoying to mange by programming, you know!
Case 1) Given three numbers
Use them all
Case 2) Given four numbers
Positive number is show +, Negative number is show -.
Numbers are sorted from left to right.
Case 2-1)
2-1) ---- => r (answer is negative)
2-2) ---+ => l (answer is positive)
2-3) --++ => l (answer is positive)
2-4) -+++ => r (answer is positive)
2-5) ++++ => r (answer is positive)
When a 0 is mixed in four numbers, it comes between
- and +.
Case 2-2)
Suppose smallest + was actually 0.
2-1) ---- => r (answer is negative)
2-2) ---0 => l (answer is 0)
2-3) --0+ => l (answer is positive)
2-4) -0++ => r (answer is 0)
2-5) 0+++ => r (answer is positive)
Case 2-3)
Suppose largest - was actually 0.
2-1) ---0 => r (answer is 0)
2-2) --0+ => l (answer is positive)
2-3) -0++ => l (answer is 0)
2-4) 0+++ => r (answer is positive)
2-5) ++++ => r (answer is positive)
Case 2-4)
If more than two 0 is mixed, products becomes always 0 because
-00+
Summary for Case 2)
answer is consistent among Case 2-1 ~ 2-4.
2-1) r (negative or 0)
2-2) l (0 or positive)
2-3) l (0 or positive)
2-4) r (0 or positive)
2-5) r (positive)
So, we do not need to worry about 0 actually.
Case 3) More than four numbers
The same with Case 2
u have to consider 3 cases:
1. max 3 positive elements can be the first answer(say 10*20*70).
2. max positive elements multiplied by 2 most negative answers is another candidate(say20*-40*-60).
3.in case where all array elements are negative,3 elements with minimum negative magnitude is answer(-1*-2*-3 in [-1,-2,3,-4,-5]).
for simplicity of question we can merge 1st and 3rd case.
find 3 maximum elements of array, similarly find 2 minimum elements of array.
u will get 2 candidates. Print the maximum of those candidates.
C++ Code:
#include <iostream>
#include <limits.h>
using namespace std;
int main()
{
int n; cin>>n; int arr[n]; for(int a=0;a<n;a++) cin>>arr[a];
bool flag=0;
int max1=INT_MIN,max2=INT_MIN,max3=INT_MIN;
int min1=INT_MAX,min2=INT_MAX;
for(int a=0;a<n;a++)
{
if(arr[a]>max1) {max3=max2; max2=max1; max1=arr[a];}
else if(arr[a]>max2) {max3=max2; max2=arr[a];}
else if(arr[a]>max3) max3=arr[a]; flag=1;
if(arr[a]<min1) {min2=min1; min1=arr[a];}
else if(arr[a]<min2) min2=arr[a];
}
int prod1=INT_MIN,prod2=INT_MIN;
if(max1>INT_MIN && max2>INT_MIN && max3>INT_MIN) prod1=max1*max2*max3;
if(max1>INT_MIN && min1<INT_MAX && min2<INT_MAX) prod2=max1*min1*min2;
cout<<max(prod1,prod2)<<endl;
}
// Here is a simple java program to find the maximum product of three numbers in an array.
import java.util.*;
import java.lang.*;
class MOHAN_BERA
{
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
System.out.println("enter the lenth of array:");
int num1=s.nextInt();
int[] num2=new int[num1];
System.out.println("enter the numbers of array:");
for(int i=0;i<num1;i++)
{
num2[i]=s.nextInt();
}
Arrays.sort(num2);//sort the array
long max1=num2[num1-1]*num2[num1-2]*num2[num1-3];//Three last numbers, can be three positive numbers
long max2=num2[num1-1]*num2[0]*num2[1];//last numbers and first two numbers,can be first two negetive and last one positive numbers
long max3=num2[0]*num2[1]*num2[2];//for all negetives numbers
long max=max1;//max1 greatest
if(max<max2 && max3<max2) //max2 greatest
{
max=max2;
}
else if(max<max3 && max2<max3)//max3 greatest
{
max=max3;
}
System.out.println(max);
}
}
in JavaScript
function largestProduct(ints) {
ints.sort((a, b) => b - a);
return ints[0] * ints[1] * ints[2];
}
Language - C#
Greedy Approach
Time Complexity O(n)
public static int GetHighestProductOfThree(int[] arrayOfInts)
{
if (arrayOfInts.Length < 3)
{
throw new ArgumentException("Array should be atleast 3 items", nameof(arrayOfInts));
}
int highest = Math.Max(arrayOfInts[0], arrayOfInts[1]);
int lowest = Math.Min(arrayOfInts[0], arrayOfInts[1]);
int highestProductOf2 = arrayOfInts[0] * arrayOfInts[1];
int lowestProductOf2 = arrayOfInts[0] * arrayOfInts[1];
int highestProductOf3 = arrayOfInts[0] * arrayOfInts[1] * arrayOfInts[2];
for (int i = 2; i < arrayOfInts.Length; i++)
{
int current = arrayOfInts[i];
highestProductOf3 = Math.Max(Math.Max(
highestProductOf3,
current * highestProductOf2),
current * lowestProductOf2);
highestProductOf2 = Math.Max(Math.Max(
highestProductOf2,
current * highest),
current * lowest);
lowestProductOf2 = Math.Min(Math.Min(
lowestProductOf2,
current * highest),
current * lowest);
highest = Math.Max(highest, current);
lowest = Math.Min(lowest, current);
}
return highestProductOf3;
}
Thanks to interviewcake.com
Detailed Explanation of this Algorithm
def solution(A):
if len(A) < 3:
return 0
A.sort()
product = A[len(A)-1] * A[len(A)-2] * A[len(A)-3]
if A[0] < 0 and A[1] < 0:
if A[0] * A[1] * A[len(A)-1] > product:
product = A[0] * A[1] * A[len(A)-1]
return product
Below is my solution in JavaScript:
function solution(A) {
A = A.sort((a, b) => b - a);
var product = A[0] * A[1] * A[2];
var length = A.length;
if (A[0] < 0) return product;
if (A[length - 1] * A[length - 2] * A[0] > product) {
return A[length - 1] * A[length - 2] * A[0];
}
if (A[2] < 0 && length >= 5 && A[3] * A[4] < A[0] * A[1]) {
return A[2] * A[3] * A[4];
}
return product;
}
This Solution is applicable only if there are 3 numbers needed. If It's dynamic or say user can ask for 4 or 5 then this solution is not suitable for it.
Without sorting you can achieve it by find out max 3 numbers from array and multiply 3 numbers, because max product requires max number from array.
public class FindOutProductPair {
public static void main(String args[]) {
int arr[]= {2,4,3,6,12,1};
// int arr1[]= {2,4,3,7,6,5,1};
// int arr1[]= {-1,-4,3,7,6,5,1};
int arr1[]= {3,2};
int max1=1,max2=1,max3=1;
for(int i=0;i<arr1.length;i++) {
if(max1 < arr1[i]) {
max3=max2;
max2=max1;
max1=arr1[i];
}else {
if(max2 < arr1[i]) {
max3=max2;
max2=arr1[i];
}
else {
if(max3< arr1[i]) {
max3=arr1[i];
}
}
}
}
System.out.println((max3+" "+max2+" "+max1)+" <-- "+(max3*max2*max1));
}
}
Could be like this in JAVA:
public final static int maxProizvedenieTrexChisel(Integer m []){
Arrays.sort(m,(g,g1)->g-g1);
System.out.println(Arrays.toString(m));
int mx1=m[0]*m[1]*m[2];
int mx2=m[m.length-1]*m[m.length-2]*m[m.length-3];
int mx3=m[0]*m[1]*m[m.length-1];
if(mx1>mx2&mx1>mx3)
return mx1;
else if(mx2>mx1&mx2>mx3)
return mx2;
return mx3;
}
could be solve using 5 variables with O(n) pass.
Max Product can be formed by either:
1. Max1 * Max2 * Max3
2. Max1 * Min1 * min2
where Max is maximum element and Min stands for minimum.
Here is my Java solution:
int maxProduct(int[] arr) {
int max1, max2, max3 = Integer.MIN_VALUE;
max1 = max3;
max2 = max3;
int min1 = Integer.MAX_VAULE;
int min2 = Integer.MAX_VAULE;
for(int n : arr) {
if (n <= min1) { // n is smaller than all
min2 = min1;
min1 = n;
} else if (n < min2) { // n lies between min1 and min2
min2 = n;
}
if (n >= max1) { // n is greater than all
max3 = max2;
max2 = max1;
max1 = n;
} else if (n >= max2) { // n lies betweeen max1 and max2
max3 = max2;
max2 = n;
} else if (n > max3) { // n lies betwen max2 and max3
max3 = n;
}
}
}
JavaScript code
function solution(A) {
if(A.length<3){
return 0;
}
let maxElement = Number.NEGATIVE_INFINITY;
let idx = null;
for(let i=0;i<A.length;i++){
if(A[i]>maxElement){
maxElement = A[i];
idx = i;
}
}
A.splice(idx,1);
A.sort((a,b)=>b-a);
let n = A.length;
let positiveMax = A[0]*A[1]*maxElement;
let negativeMax = A[n-1]*A[n-2]*maxElement;
return Math.max(positiveMax,negativeMax);
}
You can use inbuilt sort function of Javascript.Need to careful while finding max triplet product as in case of array with -ve numbers product will be combination first 2 and last and in case all +ve last 3 number product will be result.You can refer my jsfiddle. Also complexity of this algorithm is O(nlogn)
var arr=[-10, 3, 5, 6, -20];
function maxTripletProduct(data)
{
var sortedarr=data.sort(function(a,b){
return a-b;
})
console.log(sortedarr);
let length=sortedarr.length;
let product1 = sortedarr[length-3]*sortedarr[length-2]*sortedarr[length-1]
let product2=sortedarr[0]*sortedarr[1]*sortedarr[length-1];
if(product2>product1)
console.log(product2);
else
console.log(product1);
}
maxTripletProduct(arr);
Ok so my problem is basically, I have a matrix for example
010
101
111
just random 1s and 0s. So I have arrays that are rowcount and colcount, which count the number of ones in each row and column. So rowcount for this is {1,2,3} and colcount is {2,2,2}. Now in another method, I am given the arrays rowcount and colcount, and in that method, I am supposed to create a matrix with the counts in rowcount and colcount, but the end matrix can be different. Than the original. I think I'm supposed to exhaust all permutations until a matrix works. The base case must stay the same.
Note: Math.random cannot be used.
private static void recur(int[][] m, int[] rowcount, int[] colcount, int r, int c)
//recursive helper method
{
if(compare(m, rowcount, colcount)) //base case: if new matrix works
{
System.out.println();
System.out.println("RECREATED");
display(m, rowcount, colcount); //we're done!
System.exit(0);
}
else
{
int[] temp_r = new int[m.length];
int[] temp_c = new int[m[0].length];
count(m, temp_r, temp_c);
if(rowcount[r] > temp_r[r] && colcount[c] > temp_c[c])
m[r][c] = 1;
if(r+1 < m.length)
recur(m,rowcount,colcount,r+1,c);
if(rowcount[r] < temp_r[r] || colcount[c] < temp_c[c])
m[r][c] = 0;
if(c+1 < m[0].length)
recur(m,rowcount,colcount,r,c+1);
}
}
private static boolean compare(int[][] m, int[] rowcount, int[] colcount)
{
int[] temp_r = new int[m.length];
int[] temp_c = new int[m[0].length];
count(m, temp_r, temp_c);
for (int x = 0; x < temp_r.length; x++)
{
if(temp_r[x] != rowcount[x])
return false;
}
for (int y = 0; y < temp_c.length; y++)
{
if(temp_c[y] != colcount[y])
return false;
}
return true;
}
public static void count(int[][] matrix, int[] rowcount, int[] colcount)
{
for(int x=0;x<matrix.length;x++)
for(int y=0;y<matrix[0].length;y++)
{
if(matrix[x][y]==1)
{
rowcount[x]++;
colcount[y]++;
}
}
}
Well, I decided I'd implement a solution, but instead of Java (which you haven't actually specified the solution needs to be in), I'm going to use Groovy (which is Java based anyway)! I've tried to use Java syntax where possible, it's not hard to extrapolate the Java code from this (but it is much more verbose!)
Note:
*Generating a random bit matrix, not using Math.random()
*I'm storing my matrix in a string i.e. [[0,1],[1,0]] = "0110"
*My solution relies heavily, on converting Integers to/from BinaryStrings (which is essentially what your matrix is!)
// Generate random matrix
int colSize = 3;
int rowSize = 4;
String matrix = '';
for (int i = 0; i < rowSize; i++){
String bits = Integer.toBinaryString(System.currentTimeMillis().toInteger());
matrix += bits.substring(bits.length() - colSize);
Thread.sleep((System.currentTimeMillis() % 1000) + 1);
}
def (cols1,rows1) = getCounts(matrix, colSize)
println "matrix=$matrix rows1=$rows1 cols1=$cols1"
// Find match (brute force!)
int matrixSize = colSize * rowSize
int start = 0
int end = Math.pow(Math.pow(2, colSize), rowSize) // 2 is number of variations, i.e. 0 and 1
for (int i = start; i <= end; i++){
String tmp = leftPad(Integer.toBinaryString(i), matrixSize, '0')
def (cols2,rows2) = getCounts(tmp, colSize)
if (cols1 == cols2 && rows1 == rows2){
println "Found match! matrix=$tmp"
break;
}
}
println "Finished."
String leftPad(String input, int totalWidth, String padchar){ String.format('%1$' + totalWidth + "s", input).replace(' ',padchar) }
int[][] getCounts(String matrix, int colSize){
int rowSize = matrix.length() / colSize
int[] cols = (1..colSize).collect{0}, rows = (1..rowSize).collect{0}
matrix.eachWithIndex {ch, index ->
def intval = Integer.parseInt(ch)
cols[index % colSize] += intval
rows[(int)index / colSize] += intval
}
[cols,rows]
}
Gives output:
matrix=001100011000 rows1=[1, 1, 2, 0] cols1=[1, 1, 2]
Found match! matrix=001001110000
Finished.
Brute force search logic:
Given a rowcount of [1,2,3]
And a colcount of [2,2,2]
Iterate over all matrix combinations (i.e. numbers 0 - 511 i.e. "000000000" -> "111111111")
Until the new matrix combination's rowcount and colcount matches the supplied rowcount and colcount
OK, your question and comments indicate you are on the right track. The code itself is a bit messy and it has obviously gone through some iterations. That's not great, but it's OK.
You are right, I believe, that you have to 'exhaust' the recursion until you find a new result that matches the existing column/row counts. So, attack the problem logically. First, create a method that can compare a matrix with a row/column count. You call it 'compare(...)'. I assume this method you have already works ;-). This is the method that marks the end of the recursion. When compare returns true, you should return up the recursion 'stack'. You should not do a System.exit(...).
So, the basic rule of recursion, you need an input, output, a method body that contains an exit-condition check, and a recursive call if the condition is not met....
Your problem has a specific issue which complicates things - you need to make copies if the input matrix every time you go down a recursion level. Alternatively you need to 'undo' any changes you make when you come up a level. The 'undo' method is faster (less memory copies).
So, the process is as follows, start with an all-zero matrix. Call your recursive function for the all-zero start point.
int[][] matrix = new int[width][height];
int rpos = 0;
boolean found = recur(matrix, rowcount, colcount, 0, 0);
This is how it will be called, and found will be true if we found a solution.
The difference here from your code is that recur now returns a boolean.
So, our recur method needs to do:
1. check the current matrix - return true if it matches.
2. make meaningful changes (within the limits that we've added)
3. recursively check the change (and add other changes).
Your method does not have an output, so there's no way to escape the recursion. So, add one (boolean in this case).
The way this can work is that we start in the top left, and try it with that bit set, and with it unset. For each contition (set or unset) we recursively test whether the next bit matches when set, or unset, and so on.... :
private static boolean recur(int[][] m, int[] rowcount, int[] colcount,
int row, int col) {
if (compare(m, rowcount, colcount)) {
// our matrix matches the condition
return true;
}
if (row >= m.length) {
return false;
}
int nextcol = col + 1;
int nextrow = row;
if (nextcol >= m[row].length) {
nextcol = 0;
nextrow++;
if (nextrow > m.length) {
return false;
}
}
// OK, so nextrow and nextcol are the following position, and are valid.
// let's set our current position, and tell the next level of recursion to
// start playing from the next spot along
m[row][col] = 1;
if (recur(m, rowcount, colcount, nextrow, nextcol)) {
return true;
}
// now unset it again
m[row][col] = 0;
if (recur(m, rowcount, colcount, nextrow, nextcol)) {
return true;
}
return false;
}
The above code is just hand-written, it may have bugs, etc. but try it. The lesson in here is that you need to test your consitions, and you need a strategy....
I have source array, and I want to generate new array from the source array by removing a specified number of elements from the source array, I want the elements in the new array to cover as much as possible elements from the source array (the new elements are uniformly distributed over the source array) and keeping the first and last elements the same (if any).
I tried this :
public static void printArr(float[] arr)
{
for (int i = 0; i < arr.length; i++)
System.out.println("arr[" + i + "]=" + arr[i]);
}
public static float[] removeElements(float[] inputArr , int numberOfElementToDelete)
{
float [] new_arr = new float[inputArr.length - numberOfElementToDelete];
int f = (inputArr.length ) / numberOfElementToDelete;
System.out.println("f=" + f);
if(f == 1)
{
f = 2;
System.out.println("f=" + f);
}
int j = 1 ;
for (int i = 1; i < inputArr.length ; i++)
{
if( (i + 1) % f != 0)
{
System.out.println("i=" + i + " j= " + j);
if(j < new_arr.length)
{
new_arr[j] = inputArr[i];
j++;
}
}
}
new_arr[0] = inputArr[0];
new_arr[new_arr.length - 1] = inputArr[inputArr.length - 1];
return new_arr;
}
public static void main(String[] args)
{
float [] a = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
a = removeElements(a, 6);
printArr(a);
}
I have made a test for(removeElements(a, 5) and removeElements(a, 4) and removeElements(a, 3)) but removeElements(a, 6); gave :
arr[0]=1.0
arr[1]=3.0
arr[2]=5.0
arr[3]=7.0
arr[4]=9.0
arr[5]=11.0
arr[6]=13.0
arr[7]=15.0
arr[8]=0.0
arr[9]=16.0
the problem is (arr[8]=0.0) it must take a value ..
How to solve this? is there any code that can remove a specified number of elements (and keep the elements distributed over the source array without generating zero in some elements)?
EDIT :
examples :
removeElements(a, 1) ==> remove one element from the middle (7) {1,2,3,4,5,6,7,9,10,11,12,13,14,15,16}
removeElements(a, 2) ==> remove two elements at indexes (4,19) or (5,10) or (4,10) (no problem)
removeElements(a, 3) ==> remove three elements at indexes (4,9,14) or (4,10, 15) or(no problem also)
removeElements(a, 4) ==> remove four elements at indexes (3,7,11 , 15) or ( 3 ,7,11,14) for example ..
what I want is if I draw the values in the source array on (chart on Excel for example) and I draw the values from the new array , I must get the same line (or close to it).
I think the main problem in your code is that you are binding the selection to
(inputArr.length ) / numberOfElementToDelete
This way you are not considering the first and the last elements that you don't want to remove.
An example:
if you have an array of 16 elements and you want to delete 6 elements it means that the final array will have 10 elements but, since the first and the last are fixed, you'll have to select 8 elements out of the remaining 14. This means you'll have to select 8/14 (0,57) elements from the array (not considering the first and the last).
This means that you can initialize a counter to zero, scan the array starting from the second and sum the value of the fraction to the counter, when the value of the counter reach a new integer number (ex. at the third element the counter will reach 1,14) you'll have an element to pick and put to the new array.
So, you can do something like this (pseudocode):
int newLength = originalLength - toDelete;
int toChoose = newLength - 2;
double fraction = toChoose / (originalLength -2)
double counter = 0;
int threshold = 1;
int newArrayIndex = 1;
for(int i = 1; i < originalLength-1; i++){
**counter += fraction;**
if(integerValueOf(counter) == threshold){
newArray[newArrayIndex] = originalArray[i];
threshold++;
**newArrayIndex++;**
}
}
newArray[0] = originalArray[0];
newArray[newArray.length-1] = originalArray[originalArray.length-1];
You should check for the particular cases like originalArray of length 1 or removal of all the elements but I think it should work.
EDIT
Here is a Java implementation (written on the fly so I didn't check for nulls etc.)
public class Test {
public static void main(String[] args){
int[] testArray = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int[] newArray = remove(testArray, 6);
for(int i = 0; i < newArray.length; i++){
System.out.print(newArray[i]+" ");
}
}
public static int[] remove(int[] originalArray, int toDelete){
if(toDelete == originalArray.length){
//avoid the removal of all the elements, save at least first and last
toDelete = originalArray.length-2;
}
int originalLength = originalArray.length;
int newLength = originalLength - toDelete;
int toChoose = newLength - 2;
int[] newArray = new int[newLength];
double fraction = ((double)toChoose) / ((double)originalLength -2);
double counter = 0;
int threshold = 1;
int newArrayIndex = 1;
for(int i = 1; i < originalLength-1; i++){
counter += fraction;
if(((int)counter) == threshold ||
//condition added to cope with x.99999999999999999... cases
(i == originalLength-2 && newArrayIndex == newLength-2)){
newArray[newArrayIndex] = originalArray[i];
threshold++;
newArrayIndex++;
}
}
newArray[0] = originalArray[0];
newArray[newArray.length-1] = originalArray[originalArray.length-1];
return newArray;
}
}
Why cant you just initialize i=0
for (int i = 0; i < inputArr.length; i++) {
if ((i + 1) % f != 0) {
Following is the output:
arr[0]=1.0
arr[1]=1.0
arr[2]=3.0
arr[3]=5.0
arr[4]=7.0
arr[5]=9.0
arr[6]=11.0
arr[7]=13.0
arr[8]=15.0
arr[9]=16.0
This is Reservoir sampling if I understand it right i.e from a large array, create a small array by randomly choosing.