3

I have data of different items in a different restaurants

    Rest    Item     Price
    ----------------------
    ABC     dosa      14
    ABC     idly      30
    ABC     idly+upma 25

    123     dosa      30
    123     idly      7
    123     upma      12

    XYZ     dosa      20
    XYZ     idly      12
    XYZ     upma      20
    XYZ     dosa+upma 30
    XYZ     dosa+idly+upma 40

Now I need to pickup a restaurant which gives me the best deal of "dosa+idly+upma" items.

From the above example: it will be restaurant "ABC"

I am unable to design efficient way of doing this or not getting idea on how to do? Any idea?

Update

Here how my objects look like

Class Rest{
  Map<String,Integer> menu; //item,price map
}
RaceBase
  • 18,428
  • 47
  • 141
  • 202
  • dosa+idly+upma ABC with price Rs 39 rght ?? – Kick Jan 29 '14 at 09:47
  • Give us some constraints. What is the size of the input? How many items are you expected to search for? how many items are there? how many restaurants are there? – amit Jan 29 '14 at 09:49
  • @amit, this is sample data. Expected it to be time efficient. There's are not much constraints on the data. – RaceBase Jan 29 '14 at 09:54
  • 1
    @Reddy Then what's the scale? dozens? hundreds? thousands? millions? If it's only a few dozens - don't bother optimizing it too much. If it's millions - well, it's an entirely different story. – amit Jan 29 '14 at 09:54
  • Do you mean the you need an optimal max price for all 3 items, or is the price for one item more important than another (for example cheap dosa is more important than cheap upma)? Also I am getting hungry. – HectorLector Jan 29 '14 at 09:58
  • @Amit, it's less than 100. Its not about large data. – RaceBase Jan 29 '14 at 10:09
  • @HectorLector, min price of all 3 items. If I can't get any one of the item that I need, just ignore it. I must get all the items i want at minimum price – RaceBase Jan 29 '14 at 10:10
  • If it's really very small data, go greedy as suggested in mine and several other posts. Be aware computation time will rise exponentially with larger data! – LastFreeNickname Jan 29 '14 at 10:12
  • @LastFreeNickname, can you put some working code/psuedo code. I am aware that it will be exponential. not able to start :( – RaceBase Jan 29 '14 at 10:14
  • @Reddy Added a pseudocode sketch for you to my post. It's ugly though... – LastFreeNickname Jan 29 '14 at 10:38

5 Answers5

7

This problem is NP-Hard. I will show a reduction from the Set Cover Problem.

Set Cover Problem (SCP):
Given a universe of elements U (in your example U={dosa,idly,upma}) and a set of subsets of U, let it be S (for example S={{dosa}, {idly,upma}, {upma}}) Find the smallest number of subsets of S such that their union equals U.

The reduction:
Given a Set Cover Problem with U and S, create an instance of your problem with one restaurant, such that the price of each item in S (which is a set of one or more items) is 1.

Now, given an optimal solution to your problem - the minimal price possible, is basically the minimal number of subsets needed to cover the 'universe'.
Given an optimal solution to the set cover problem - the number of sets needed is the minimal price of the subset.

Conclusion:
Since we have seen that solving this problem efficiently will solve SCP efficiently, we can conclude that the problem is NP-Hard, and thus there is no known polynomial solution to it (and most believe one does not exist).

Alternatives are using a heuristic solution or a brute force one (just search all possibilities, in exponential time).

amit
  • 175,853
  • 27
  • 231
  • 333
  • That's what I said. :-) Though I compared it to Rucksack and TSP, which are in the same class. – LastFreeNickname Jan 29 '14 at 10:16
  • @LastFreeNickname Yea, I see you did now. But I added a formal proof - I think there is an added value on it, won't you say? – amit Jan 29 '14 at 10:17
1

A sketch of one possible greedy algorithm is:

  1. Iterate through all unary offers (e.g. dosa, idly or upma) to find the minimum of each.
  2. Iterate through all binaray (e.g. idly+upma) / tertiary (...) offers, compare if it's cheaper than the unary offers and replace if so.

You will have to code the offer deparsing still, but it shouldn't be that hard. This algorithm will find good, but not necessary the best solution, and might work on very small smaples.

Actually, your problem compares to the Rucksack or TSP problems, which are NP-complete and thus only solvable in exponentially time. If you want a solution for that, consider reading a lot of papers and coding even more. That's the holy grail of computer science. ;-)

UPDATE: On request of the TO here's some exponentially pseudocode sketch:

foreach restaurant
    create a list of all possible combinations of the offers // here's the exp!
    filter those combinations that hold more/less than 1 dosy/idly/umpa
    select minimum of the remaining ones

Comment: This is really ugly, yuk! :-(

LastFreeNickname
  • 1,445
  • 10
  • 17
  • there must be a more efficient solution then brute-forcing the entire data base for each query... – amit Jan 29 '14 at 09:55
  • @LastFreeNickname, as I am not looking at large scale and I am aware that it's NP problem for long data or it's not going to handle large data. for this simple data, assume I don't have more than 5 restaurants and not more than 10 items per restaurant. – RaceBase Jan 29 '14 at 10:12
  • @Reddy Then your only possibility is to brute force it as explained above and hope your data never grows big. :-) You might consider an even dirtier approach than mine to keep your algorithm simple. – LastFreeNickname Jan 29 '14 at 10:17
1

try

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Mult {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Map<String,List<X>> xx = new HashMap<String,List<X>>();
        xx.put("ABC",new ArrayList<X>());
        xx.get("ABC").add(new X("", 0));
        xx.get("ABC").add(new X("dosa", 14));
        xx.get("ABC").add(new X("idly", 30));
        xx.get("ABC").add(new X("idly+upma", 25));


        xx.put("123",new ArrayList<X>());
        xx.get("123").add(new X("", 0));
        xx.get("123").add(new X("dosa", 30));
        xx.get("123").add(new X("idly", 7));
        xx.get("123").add(new X("upma", 12));


        xx.put("XYZ",new ArrayList<X>());
        xx.get("XYZ").add(new X("", 0));
        xx.get("XYZ").add(new X("dosa", 20));
        xx.get("XYZ").add(new X("idly", 12));
        xx.get("XYZ").add(new X("upma", 20));
        xx.get("XYZ").add(new X("dosa+upma", 30));
        xx.get("XYZ").add(new X("dosa+idly+upma", 40));

        String[] t = {
                "dosaidlyupma",
                "idlydosaupma",
                "upmaidlydosa",
                "dosaupmaidly",
                "upmadosaidly",
                "idlyupmadosa"};
        Set<String> targets = new HashSet<String>(Arrays.asList(t));

        Map<String,Integer> best = new HashMap<String,Integer>();

        for(String restaurant:xx.keySet()){
            best.put(restaurant, Integer.MAX_VALUE);
            String combo = null;
            for(X a:xx.get(restaurant)){
                int deal = a.price;
                combo = a.item;
                for(X b:xx.get(restaurant)){
                    deal = deal + b.price;
                    combo = combo + "+" + b.item;
                    for(X c:xx.get(restaurant)){
                        deal = deal + c.price;
                        combo = combo + "+" + c.item;
                        if (targets.contains(combo.replaceAll("\\+", ""))){
//                          System.out.println(restaurant+"\t"+combo.replaceAll("\\+", "")+"\t"+deal);
                            if (best.get(restaurant) > deal){
                                best.put(restaurant, deal);                 
                            }
                        }
                    }
                }
            }
        }

        System.out.println(best);
    }

}

will give you

{XYZ=40, ABC=39, 123=49}

it's the old good brute force approach.

not the best one, but for this small set, it works.

Leo
  • 6,480
  • 4
  • 37
  • 52
0
  1. Calculate the possible price combinations: Iterate through the map and parse the strings. Store each price combination.

  2. Filter out the more expensive prices.

  3. Compare the remaining prices at each restaurant, and return the restaurant with the cheapest price.

You could also do some checks to minimise iterations, e.g:

  • If idly is > than idly+umpa, don't calculate the any combinations involving idly.
Someone
  • 551
  • 3
  • 14
  • Thought so the first time too, but as soon as you include the >= 1 item offers in this approach the order of comparisons will change the result. Said: idly+upma might block a dosa+idly replacement and vice versa. See my post for further explanations. – LastFreeNickname Jan 29 '14 at 10:04
  • @LastFreeNickname I'm not sure I understand you. Are you referring to step 2? – Someone Jan 29 '14 at 10:07
  • If I understand the thread opener, the task is also to replace min_upma and min_idly with min_upmaidly. This part it missing. – LastFreeNickname Jan 29 '14 at 10:10
0

First you need to make all the possible combination of 3 items corresponding to different restaurents like :

XYZ     dosa+idly+upma 52
XYZ     dosa+upma+idly 42
XYZ     dosa+idly+upma 40

Same the above case with all the restaurants.

Then sort the price and let the minimum price restra WIN.

Kick
  • 4,823
  • 3
  • 22
  • 29
  • Assuming 3 is only an example, and you might have more items in a restaurant and you will ask only for a portion of them - this approach requires to create `2^n` different sets per restaurant, where `n` is the total number of elements if you want to do it in pre-processing. – amit Jan 29 '14 at 10:02
  • @amit as per the requirment i have provided the answer. – Kick Jan 29 '14 at 10:03
  • The requirement didn't say 3 items. the example did. – amit Jan 29 '14 at 10:04
  • How could i knw its example or requirement ? If you want to scale the items then also u need to use the same logic. – Kick Jan 29 '14 at 10:07