0

I am trying to solve a problem with combinations:

I receive an array of products which include an availability percentage and have to calculate the most efficient and cheapest combination that add up to get a given availability percentage. There are some rules that make it harder.

Given the following problem:

[{
  "name": "network 1",
  "availability": 0.5,
  "cost": 20, 
 },               
 {
  "name": "network 2",
  "availability": 0.75,
  "cost": 30,
 }]

I need to find the most efficient and cheap combination using the following equation to calculate the availability: 1 - (1 - availability A) * (1 - availability B)… * (1 - availability n)

Requirements/rules:

  • The total availability is 0.9999 (i.e 99.99%)
  • Repetition is allowed
  • Should be the cheapest combination cost-wise

At the end it should print the items used to get to the solution as well as the total cost.

I have tried solving it using the bounded/unbounded knapsack problem as well as min cost path and subset sum, but couldn't figure out a solution. Also at the end it only prints the supposed solution, but I don't know how to get the items used to get to that solution.

At the end I went back to the unbounded knapsack since it has repetitions and I got this so far:

private static int max(int i, int j) {
        return (i > j) ? i : j;
    }

    private static int unboundedKnapsack(int W, int n, List<Long> val, List<Long> wt) {
        int dp[] = new int[W + 1];

        for(int i = 0; i <= W; i++){
            for(int j = 0; j < n; j++){
                if(wt.get(j) <= i){
                    dp[i] = max(dp[i], Math.toIntExact(dp[(int) (i - wt.get(j))] + val.get(j))); // TODO: can't figure out how to implement 1 - (1 - availability A) * (1 - availability B)… * (1 - availability n) instead.
                }
            }
        }

        return dp[W];
    }

    public static void main(String[] args) {
        String problem = "" +
                "[{\"function\":\"Server\", " +
                "\"name\":\"server 1\", " +
                "\"availability\":5, " +
                "\"cost\":10 }, " +

                "{\"function\":\"Server\", " +
                "\"name\":\"server 2\", " +
                "\"availability\":10, " +
                "\"cost\":30 }, " +

                "{\"function\":\"Server\", " +
                "\"name\":\"server 3\", " +
                "\"availability\":15, " +
                "\"cost\":20 }] ";

        JSONParser parser = new JSONParser();

        try{
            Object obj = parser.parse(problem);
            JSONArray array = (JSONArray) obj;

            List<Long> valArray = new ArrayList<>();
            List<Long> wtArray = new ArrayList<>();
            for (Object value : array) {
                JSONObject o = (JSONObject) value;

                Long cost = (Long) o.get("cost");
                valArray.add(cost);

                Long availability = (Long) o.get("availability");
                wtArray.add(availability);

            }

            int W = 100; // TODO: should be the required availability i.e 0.9999. Can't figure out how to get it to work with double values
            int n = valArray.size();
            System.out.println("cost: " + unboundedKnapsack(W, n, valArray, wtArray));
        } catch(ParseException pe) {
            System.out.println("position: " + pe.getPosition());
            System.out.println(pe);
        }
    }

Currently I only get the cost as output. So if you run the code ubove you should get:

cost: 300

However the output I'm trying to achieve is something like this:

availability: 0.9999
items: server 1, server 2...
cost: 300

If anyone has an idea of an algorithm to fix this, I would really appreciate it.

Kirito
  • 3
  • 1
  • 4

1 Answers1

0

This can be solved with dynamic programming using an idea similar to the one from Is there any algorithm to address the longest common subsequent problem with different weights for each character?.

The idea is that you build up a table of entries ordered by by increasing cost, and increasing availability. Each entry contains the following data:

total_cost: ...
total_availability: ...
last_item: ...
previous_entry:

Note the previous_entry is recursively the same kind of data, so from any entry you can follow the chain back to get all of the items that you include.

Your table starts off with one entry:

{total_cost: 0, total_availability: 0.0, last_item: null, previous_entry: null}

For each item with non-zero availability you take each existing element in the table, and recursively build up a chain of entries with availability up to the target. You then sort all of the entries (both there before, and new ones created) by ascending cost then descending availability. You then rebuild the table with only those elements such that no element of equal or lower cost has higher availability, with all elements whose availability exceeds the target being counted as being at the target availability.

After going through all of the items, the entry in the table with at least the target availability will be your answer, and you can recursively trace through previous_entry to figure out which items and how many of them were required to find it.

This will take time that is polynomial in the number of entries and how many possible costs there are that are needed.

btilly
  • 43,296
  • 3
  • 59
  • 88