Creating optimal shopping list based on given conditions - java

I am trying to create shopping lists from a collection of products, where the returned shopping list should be optimized for cost as well as to meet another condition.
For example, let's say that I want to create shopping lists based on the energy content of the products. When the user enters a total sum, the returned shopping list should try to max out the kcal content while keeping the total sum at or around the sum specified by the user.
I've gotten so far as to create the collection of products, and all products are stored as objects with fields holding nutritional values and price etc. The kcal-value is also stored as a member variable in each product's object.
At first I considered looping through all combinations of products, sort out those that are way out of the price interval, and then return the one with the highest kcal content. But as the numbers of products available increases this soon becomes a non-viable option I think.
I now wonder if there is any algorithm to solve this problem, if not, is there any way to easily implement this?

I did something similiar with Dynamic Programming Wikipedia article on Dynamic Programming
In the following costs and values should be arrays of the same length. The length of the possible items to choose from. The capacity is the maximum sum of all the costs of items you choose. It returns an array of booleans of the same length determining whether to take the item or not.
The idea is to create a table with the solutions to sub-problems that can then be used to solve a bigger problem. The sub-problems just solve the same problem but with a smaller list, initially with only one item. The table contains the best value you can get with the first i items for each weight up to the maximum. As you add one potential item to the list you add its value to the previous solution for that allowed weight minus the weight your adding and check if that is better than the previous solution without the last item. Once the last row is created you can tell which items to take by checking for where there are differences in the values in that last row.
public boolean[] solve(int[] values, int[] costs, int capacity) {
boolean take[] = new boolean[values.length];
int min_cost = Integer.MAX_VALUE;
for (int i = 0; i < values.length; i++) {
if (costs[i] < min_cost) {
min_cost = costs[i];
}
}
int table[][] = new int[values.length][capacity + 1 - min_cost];
for (int i = 0; i < values.length; i++) {
int v = values[i];
int w = costs[i];
for (int j = 0; j < capacity - min_cost + 1; j++) {
int prev_value = 0;
int new_value = 0;
if (i > 0) {
prev_value = table[i - 1][j];
if (w <= j + min_cost) {
if (w <= j) {
new_value = table[i - 1][j - w] + v;
} else {
new_value = v;
}
}
} else if (w <= j + min_cost) {
new_value = v;
}
table[i][j] = Math.max(prev_value, new_value);
}
}
int index = capacity - min_cost;
for (int i = values.length - 1; i > 0 && index >= 0; i--) {
if (table[i][index] != table[i - 1][index]) {
take[i] = true;
index -= costs[i];
if (index < 0) {
System.err.println("index = " + index);
}
} else {
take[i] = false;
}
}
take[0] = index >= 0 && table[0][index] != 0;
return take;
}

Related

How do I create a bigger matrix from another?

I'm having a problem working with matrices. I have a matrix which will hold 3 columns of data and as a starting point 10 rows of data. Each row is an article in a "cash register software" and each column is in order: {article number, items in stock, article price}.
As I mentioned, the int[][]articles has to start with the size of [10][3], but have the ability to expand with the help of a method "checkFull". The user will be asked how many articles he/she wants to add and the value will be saved into the variable "noOfArticles". The checkFull() method has to check if the value of articles fits into the article[][] matrix or if it has to create another matrix with the right amount of slots, copy the values from articles[][] and return the new matrix.
The following code is my shot at the problem, does anyone got any ideas how to get this method working properly?
public static int[][] checkFull(int[][]articles, int noOfArticles) {
int index = 0;
int i = 0;
while (i < articles.length)
{
if (articles[i][0] == 0)
{
index = i;
break;
}
i++;
}
if ((articles.length - index) < noOfArticles)
{
newmat = new int[noOfArticles + articles.length][3];
for (i = 0; i < articles.length; i++)
{
for (int j = 0; j < articles[i].length; j++)
{
int[][]newmat[i][j] = articles[i][j];
}
}
return newmat;
}
else
{
return articles;
}
}

How to find most profitable Path in 2-Dimensional Array

I'm trying to implement a game where the viable moves are down-left and down-right.
The parameter for the function is for the size of the array, so if you pass 4 it will be a 4 by 4 array.
The starting position is the top row from any column. Every element in the array is a number in the range 1-100, taken from a file. I need to find the resulting value for the most profitable route from any starting column.
My current implementation will compare the right position and left position and move to whichever is higher. The problem is, for example, if the left position is lower in value than the right, but the left position will provide more profit in the long run since it can access higher value elements, my algorithm fails.
Here is a demo:
84 (53) 40 62
*42* 14 [41] 57
76 *47* 80 [95]
If we start at number 53. The numbers enclosed in * are the moves that my algorithm will take, but the numbers enclosed in [] are the moves my algorithm should take.
This is my code:
import java.util.ArrayList;
import java.util.Scanner;
public class bestPathGame{
private int[][] grid;
private int n;
public bestPathGame(int num){
Scanner input = new Scanner(System.in);
n = num;
grid = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
grid[i][j] = input.nextInt();
}
}
}
public static void main(String[] args){
bestPathGame obj = new bestPathGame(Integer.parseInt(args[0]));
obj.bestPath();
}
private boolean moveLeftBetter(int r,int c){
if(c <= 0){
return false;
} else if (c >= n -1 ){
return true;
}
return grid[r][c-1] > grid[r][c+1];
}
public void bestPath(){
ArrayList<Integer> allOptions = new ArrayList<>();
for(int k = 0; k < n; k++){
int row = 0;
int col = k;
int collection = grid[row][col];
while(row < n - 1){
row += 1;
if(moveLeftBetter(row,col)){
col-=1;
} else{
col+=1;
}
collection += grid[row][col];
}
allOptions.add(collection);
}
System.out.println(allOptions.stream().reduce((a,b)->Integer.max(a,b)).get());
}
}
Greedy algorithm vs Dynamic programming
There's an issue with the logic of your solution.
Basically, what you are implemented is a called a greedy algorithm. At each step of iteration, you are picking a result that optimal locally, assuming that this choice will lead to the optimal global result. I.e. your code is based on the assumption that by choosing a local maximum between the two columns, you will get the correct global maximum.
As a consequence, your code in the bestPath() method almost at each iteration will discard a branch of paths based on only one next value. This approach might lead to incorrect results, especially with large matrixes.
Greedy algorithms are rarely able to give an accurate output, usually their result is somewhat close but not precise. As an upper-hand, they run fast, typically in O(n) time.
For this problem, you need to use a dynamic programming (DP).
In short, DP is an enhanced brute-force approach which cashes the results and reuses them instead of recalculating the same values multiple times. And as well, as a regular brute-force DP algorithms are always checking all possible combinations.
There are two major approaches in dynamic programming: tabulation and memoization (take a look at this post for more information).
Tabulation
While implementing a tabulation first you need to create an array which then need to be prepopulated (completely or partially). Tabulation is also called the bottom-up approach because calculation start from the elementary edge cases. Every possible outcome is being computed based on the previously obtained values while iterating over this array. The final result will usually be stored in the last cell (in this case in the last row).
To implement the tabulation, we need to create the matrix of the same size, and copy all the values from the given matrix into it. Then row by row every cell will be populated with the maximum possible profit that could be obtained by reaching this cell from the first row.
I.e. every iteration will produce a solution for a 2D-array, that continuously increases by one row at each step. It'll start from the array that consists of only one first row (no changes are needed), then to get the profit for every cell in the second row it's values has to be combined with the best values from the first row (that will be a valid solution for 2D-array of size 2 * n), and so on. That way, solution gradually develops, and the last row will contain the maximum results for every cell.
That how the code will look like:
public static int getMaxProfitTabulation(int[][] matrix) {
int[][] tab = new int[matrix.length][matrix.length];
for (int row = 0; row < tab.length; row++) { // populating the tab to preserve the matrix intact
tab[row] = Arrays.copyOf(matrix[row], matrix[row].length);
}
for (int row = 1; row < tab.length; row++) {
for (int col = 0; col < tab[row].length; col++) {
if (col == 0) { // index on the left is invalid
tab[row][col] += tab[row - 1][col + 1];
} else if (col == matrix[row].length - 1) { // index on the right is invalid
tab[row][col] += tab[row - 1][col - 1];
} else {
tab[row][col] += Math.max(tab[row - 1][col - 1], tab[row - 1][col + 1]); // max between left and right
}
}
}
return getMax(tab);
}
Helper method responsible for extracting the maximum value from the last row (if you want to utilize streams for that, use IntStream.of(tab[tab.length - 1]).max().orElse(-1);).
public static int getMax(int[][] tab) {
int result = -1;
for (int col = 0; col < tab[tab.length - 1].length; col++) {
result = Math.max(tab[tab.length - 1][col], result);
}
return result;
}
Memoization
The second option is to use Memoization, also called the top-down approach.
As I said, DP is an improved brute-force algorithm and memoization is based on the recursive solution that generates all possible outcomes, that is enhanced by adding a HashMap that stores all previously calculated results for every cell (i.e. previously encountered unique combination of row and column).
Recursion starts with the first row and the base-case of recursion (condition that terminates the recursion and is represented by a simple edge-case for which output is known in advance) for this task is when the recursive call hits the last row row == matrix.length - 1.
Otherwise, HashMap will be checked whether it already contains a result. And if it not the case all possible combination will be evaluated and the best result will be placed into the HashMap in order to be reused, and only the then the method returns.
Note that tabulation is usually preferred over memoization, because recursion has significant limitations, especially in Java. But recursive solutions are sometimes easier to came up with, so it's completely OK to use it when you need to test the idea or to prove that an iterative solution is working correctly.
The implementation will look like that.
public static int getMaxProfitMemoization(int[][] matrix) {
int result = 0;
for (int i = 0; i < matrix[0].length; i++) {
result = Math.max(result, maxProfitHelper(matrix, 0, i, new HashMap<>()));
}
return result;
}
public static int maxProfitHelper(int[][] matrix, int row, int col,
Map<String, Integer> memo) {
if (row == matrix.length - 1) { // base case
return matrix[row][col];
}
String key = getKey(row, col);
if (memo.containsKey(key)) { // if cell was already encountered result will be reused
return memo.get(key);
}
int result = matrix[row][col]; // otherwise result needs to be calculated
if (col == matrix[row].length - 1) { // index on the right is invalid
result += maxProfitHelper(matrix, row + 1, col - 1, memo);
} else if (col == 0) { // index on the left is invalid
result += maxProfitHelper(matrix, row + 1, col + 1, memo);
} else {
result += Math.max(maxProfitHelper(matrix, row + 1, col - 1, memo),
maxProfitHelper(matrix, row + 1, col + 1, memo));
}
memo.put(key, result); // placing result in the map
return memo.get(key);
}
public static String getKey(int row, int col) {
return row + " " + col;
}
Method main() and a matrix-generator used for testing purposes.
public static void main(String[] args) {
int[][] matrix = generateMatrix(100, new Random());
System.out.println("Tabulation: " + getMaxProfitTabulation(matrix));
System.out.println("Memoization: " + getMaxProfitMemoization(matrix));
}
public static int[][] generateMatrix(int size, Random random) {
int[][] result = new int[size][size];
for (int row = 0; row < result.length; row++) {
for (int col = 0; col < result[row].length; col++) {
result[row][col] = random.nextInt(1, 101);
}
}
return result;
}

Why is my ArrayList not initialized to the capacity i specify?

I keep getting a java.lang.IndexOutOfBoundsException: Index: 1288, Size: 1287
this is in reference to ArrayList<Formant> stored in the first for loop. I dont understand why the capacity of the ArrayList is being set to 1287 rather than dp.size
Can anyone help with this?
I've increased the max heap size to 10Gb
I've tried setting the initial capacity to 2048 (the max size of dp).
Relevant code is shown below:
public Formant[] analyzeBuffer(ArrayList<DataPoint> dp) {
//declare variables
int count1 = 0;
int count2 = 0;
ArrayList<DataPoint> stored = new ArrayList<>(dp.size());
Formant[] buffForm = new Formant[12];
//f = new Formant(greatest);
//control for infinit loop
//while loop checks if datapoint is above threshhold, finds the largest number, and removes all datapoints within a given formant
while (!(dp.isEmpty())) {
//checks if data point is above threshold, if yes: stores data point in new arraylist
for (DataPoint td : dp) {
if (td.returnFreq() > threshold) {
stored.add(count1, td);
count1++;
}
//checks if data point is the largest number
if (td.returnFreq() > greatest) {
greatest = td.returnFreq();
f = new Formant(greatest);
}
}
//only removes data points that are formants
//removes all data points within a given formant
if (f.isFormant) {
buffForm[count2] = f;
count2++;
for (int k = 0; k < stored.size(); k++) {
if (stored.get(k).returnFreq() <= (f.determineFormant() + 150) && stored.get(k).returnFreq() >= (f.determineFormant() - 150)) {
stored.remove(k);
}
}
}
//if freqeuncy is not formant remove all instances of that data point
else{
buffForm[count2] = f;
count2++;
for (int k = 0; k < stored.size(); k++) {
if (stored.get(k).returnFreq() == f.freq) {
stored.remove(k);
}
}
}
}
return buffForm;
}
An ArrayList's capacity is different than its size. Its size is "how many elements are in this," whereas the capacity is "how many elements can I put in before the ArrayList has to re-size its internal array?"
List#add(int idx, E element) adds an element at the given index, but it requires that the List's size (not capacity) be large enough:
[throws] IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())
stored.remove(k);
You just shrank stored, so the larger indexes are no longer valid.
You need to make your loop run backwards, so that you never try to use an index that got shifted by a removal.

Shift elements in 2D ArrayList

I have 2D ArrayList which was filled with elements (objects which contain images etc.) so the 2D array was full. After calling removing functions my array looks like the picture on the left side; on the right side you can see the desired result. Could someone please give me the idea how to reorganize my array as you can see on the picture?
The idea was to go from to bottom to top. If I find the gap (the gap means that I set the background of element to null, respectively, imageIcon is set to null) I will switch it for the previous element. And because I have switched it I have to do it for the whole column. Problem is, when they are 2 or more gaps and also, this algorithm does nothing.
for (int i = 0; i < 10; i++) {
for (int j = 7; j > 0; j--) {
Item currentItem = this.elements.get(j).get(i).getItem();
if (currentItem.getBack().getIcon() == null) {
int count = j;
while (count > 1) {
Position temp = this.elements.get(count).get(i);
Position zero = this.elements.get(count).get(i);
Position previous = this.elements.get(count - 1).get(i);
zero = previous;
previous = temp;
count--;
}
}
}
}
The arrayed data size is limited to 10x8 because of my gameboard panel. The items in array are not distinguishable, they only have different backgrounds (type of JLabel component). The items have to "fall from top to down".
PS: I am creating a clone of Bejeweled
From what you have shown in the pictures, you want the column's elements to be "dropped to bottom"? If that is the case, you should probably use a regular 2D array instead of ArrayList, with an array for each of your columns, and assume the bottom is indexed 0 - why? because ArrayList doesn't have fixed size, and your problem statement shows you want the container to be of fixed size. The solution would then be (roughly, because you shown only a part of your code, not a SSCCE):
//Item[][] items;
for( Item[] arr : items )
for( int i = arr.length - 2; i >= 0; i-- )
if ( arr[i] == null ) // or arr[i].getBack().getIcon() == null or whatever
for( int j = i; j < arr.length - 1; j++ )
arr[j] = arr[j+1];
This is a crude bubble sort, suitable for small arrays. There are other solutions possible (as this is a sorting problem by itself - you may look up qsort for this), but this one is arguably the simplest.
Note: You may implement the very same solution for ArrayLists, yet I strongly advocate against it. Using nested ArrayLists to mimic multi-dim arrays is seldom a good idea - it'll create the arrays anyway, but you'll get a large overhead, making the code both slower and less readable - anyway you can do so by replacing []s with get()/set() etc.
For the sake of reference:
//ArrayList<ArrayList<Item>> items;
//int columnHeight;
for( ArrayList<Item> arr : items )
for( int i = columnHeight - 2; i >= 0; i-- )
if ( arr.get(i) == null ) //or arr.get(i).getIcon()==null or whatever
for( int j = i; j < columnHeight - 1; j++ )
arr.set(j, arr.get(j+1));
or simply, by providing a comparator:
//ArrayList<ArrayList<Item>> items;
//int columnHeight;
for( ArrayList<Item> arr : items )
Collections.sort(arr, new Comparator<Item>() {
#Override
public int compare(Item i1, Item i2) {
return ...; // place the sorting rule here
}
});
For more info, see docs for Collections.sort() & Comparator.
Also, if this is indeed for Bejewelled clone - you may consider doing the "dropping" by doing an iteration dropping all jewels with an empty field beneath by one step, counting the amount of dropped jewels, and repeating this iteration till the amount of drops == 0. That's the algo I used in my clone in the days of the past.
As #vaxquis has already mentioned, it's better to rewrite your code in some more elegant way.
for (int i = 0; i < 10; i++) {
// Perfoming bubble sort for each column
boolean swapped = true;
for(int j = 6; j > 0 && swapped; j--) {
swapped = false;
for (int k = 0; k < j; k++) {
Item currentItem = this.elements.get(k).get(i).getItem();
Item nextItem = this.elements.get(k+1).get(i).getItem();
if (currentItem.getBack().getIcon() != nextItem.getBack().getIcon()) {
swap(currentItem, nextItem); // implement this yourself
swapped = true;
}
}
}
}
Besides the algorithm issues addressed by the other answers, your main problem is that you never change anything in the arrays. The following code just moves some values between local variables:
Position temp = this.elements.get(count).get(i);
Position zero = this.elements.get(count).get(i);
Position previous = this.elements.get(count - 1).get(i);
zero = previous;
previous = temp;
You probably have a setItem() method to set things back into a Position object? Then a swap would be:
Position current = this.elements.get(count).get(i);
Position previous = this.elements.get(count - 1).get(i);
Item temp = current.getItem();
current.setItem(previous.getItem();
previous.setItem(temp);

Edit Distance solution for Large Strings

I'm trying to solve the edit distance problem. the code I've been using is below.
public static int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
// len1+1, len2+1, because finally return dp[len1][len2]
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= len2; j++) {
dp[0][j] = j;
}
//iterate though, and check last char
for (int i = 0; i < len1; i++) {
char c1 = word1.charAt(i);
for (int j = 0; j < len2; j++) {
char c2 = word2.charAt(j);
//if last two chars equal
if (c1 == c2) {
//update dp value for +1 length
dp[i + 1][j + 1] = dp[i][j];
} else {
int replace = dp[i][j] + 1 ;
int insert = dp[i][j + 1] + 1 ;
int delete = dp[i + 1][j] + 1 ;
int min = replace > insert ? insert : replace;
min = delete > min ? min : delete;
dp[i + 1][j + 1] = min;
}
}
}
return dp[len1][len2];
}
It's a DP approach. The problem it since it use a 2D array we cant solve this problem using above method for large strings. Ex: String length > 100000.
So Is there anyway to modify this algorithm to overcome that difficulty ?
NOTE:
The above code will accurately solve the Edit Distance problem for small strings. (which has length below 1000 or near)
As you can see in the code it uses a Java 2D Array "dp[][]" . So we can't initialize a 2D array for large rows and columns.
Ex : If i need to check 2 strings whose lengths are more than 100000
int[][] dp = new int[len1 + 1][len2 + 1];
the above will be
int[][] dp = new int[100000][100000];
So it will give a stackOverflow error.
So the above program only good for small length Strings.
What I'm asking is , Is there any way to solve this problem for large strings(length > 100000) efficiently in java.
First of all, there's no problem in allocating a 100k x 100k int array in Java, you just have to do it in the Heap, not the Stack (and on a machine with around 80GB of memory :))
Secondly, as a (very direct) hint:
Note that in your loop, you are only ever using 2 rows at a time - row i and row i+1. In fact, you calculate row i+1 from row i. Once you get i+1 you don't need to store row i anymore.
This neat trick allows you to store only 2 rows at the same time, bringing down the space complexity from n^2 to n. Since you stated that this is not homework (even though you're a CS undergrad by your profile...), I'll trust you to come up with the code yourself.
Come to think of it I recall having this exact problem when I was doing a class in my CS degree...

Categories

Resources