1

I have the following objects:

enum Slot
{
   HANDS, LEGS, CHEST, HEAD, FEET;
}

class Clothing
{
   // The slot this piece of clothing is worn on.
   Slot s;
   // The color of the clothing, used for `gradeOutfit`
   Color c;
}

class Person
{
   Map<Slot, Clothing> body;

   // look through his outfit and give a score
   // for how well he looks
   int gradeOutfit()
   {
      return ...
   }
}

I have one Person object and a collection of Clothing. This collection has many Clothing objects of each Slot. For example, it might look like this:

MyCloset = { GREEN_HAT, RED_VEST, BLACK_VEST, 
BLUE_JEANS, BROWN_PANTS, RED_SHOES, BLACK_HAT, BLUE_GLOVES, PURPLE_VEST }

In the reality of my program, there are a lot more items than just these but this is just a simplified example.

Problem:

I need to find a combination of these clothes that lead to the highest gradeOutfit score. That means my Person will have to make sure he tries on every Clothing item with every other Clothing item (within limits, ex. it's impossible for two hats to be worn because both are for HEAD Slot). A Person cannot have their gradeOutfit called until they are wearing a Clothing item for every Slot.

I was thinking recursion is the best way to do this but then I think I'd get a stack overflow very fast if I had a decent amount of items. I tried doing it iteratively but I cannot seem to find a good easy way to loop through everything. My program basically looks like

Person p = new Person();
for (Clothing i : MyCloset)
{
    for (Clothing h : MyCloset)
    {
        if (i == h) continue;

        if (!p.isWearing(h.slot())
        {
            p.wear(h);
        }
    }

    int score = p.gradeOutfit();
}

But I know this is just a terrible approach. In order to ensure that every clothing item has been paired up with every other Clothing item, I would need so much more looping logic than just this. No matter what I try, it turns into spaghetti code. I also need to avoid looping over the same outfit twice and make sure that no outfit combination is forgotten about.

What is the best way to approach something like this?

Hatefiend
  • 3,416
  • 6
  • 33
  • 74
  • Is the score of a colour or clothing independent? – Aakash Verma Jul 03 '17 at 09:02
  • @AakashVerma No, the `gradeOutfit` score is a complicated algorithm and totally depends on the outfit. For example, an outfit with two `RED` items has a high score, but if the `RED` items are adjacent to eachother, it gets a low score. Assume that different outfits can lead to completely different scores. – Hatefiend Jul 03 '17 at 09:05
  • 1
    I am sorry I don't completely understand the problem, but I think for every item in slot enum you have a collection. And then you want to take a single item from each collection and call it a suit. Then the person p wears that suit, and you rate it. If thats the case, you need to make a list of all combinations and iterate through it. Making a list of all combinations is easy. See this link http://www.geeksforgeeks.org/print-all-possible-combinations-of-r-elements-in-a-given-array-of-size-n/. You will have to use some tweaks, instead of each element in input array, reference a collection item – Pushan Gupta Jul 03 '17 at 09:36
  • It depends on gradeOutfit. If it's true that every suboptimal outfit can be improved with a single swap, then you can start with any outfit, check the single-swap outfits for improvement, swap when you spot an improvement, and stop when you don't find an improvement. Each iteration takes O(possible swaps = num items of clothing not worn). If that isn't true, you can still use this to find locally good outfits (not optimal, but can't be improved with any single swap). – Dave Jul 03 '17 at 14:32

3 Answers3

1

This is an example of a mathematical optimization problem. You seem to already have the objective function (the function that calculates the gradeOutfit score - taking as an input five clothings, one per slot) and you need some constraints (e.g. each clothing in a combination of 5 belongs to a different slot). You need a Java solver library to do this. If your objective function is linear, a linear solver will do. As I have only experience with commercial solvers, I cannot recommend an open-source one, a list of options can be found here.

A simpler (but not extremely elegant) way, without a solver:

  1. Create 5 sets of Clothing objects, one per slot (you can use Java HashSet for this).
  2. Iterate over all combinations, each time taking one item from each of the 5 sets. You need n1 x n2 x n3 x n4 x n5 combinations, where ni is the number of clothing instances per slot.

It also seems to me that the gradeOutfit function should not be part of the Person class - as it is actually the property of an outfit, not a person (i.e. two persons with the same outfit have exactly the same scores). I 'd prefer to have an Outfit class and put it there.

  • Okay, gotcha. I like the idea of the five `Set`s but does this mean I would have basically four nested `for` loops (looping through each `Set`)? – Hatefiend Jul 03 '17 at 10:10
  • @Hatefiend Yes, this is not very elegant but can work if the number of items is reasonable (otherwise, the combinations might be too many to hold in memory). A more elegant way is described [here](https://stackoverflow.com/a/2419399/5102627). – Nikos Houssos Jul 03 '17 at 10:39
0

You have very poorly created the data structure.

enum Slot
{
   HANDS, LEGS, CHEST, HEAD, FEET;
   numbers = new int[values.length()]
}

enum COLOR
{
   RED,BLUE,...;
}
enum Clothing {
    GREEN_HAT(HEAD,GREEN), ...; 

    Slot slot;
    Color color;
    public static Clothing (Slot slot, Color color){...}
}
class Outfit extends Map <Slot, Clothing> {
    countScore(){};
    public static Outfit(){
        //foreach slot this.put(slot, Clothing.values().get(0));
    }
}
... 
int n=slot.values.length()-1;
Outfit currentOutfit = new Outfit();
Outfit bestOutfit = new Outfit();
int currentActiveSlot = 0;
// make a cycle for combination of all Outfits
Gangnus
  • 24,044
  • 16
  • 90
  • 149
  • 1
    My `Clothing` cannot be an enum since its members are subject to change. I didn't include that in my original post for the sake of simplicity. Also `// make a cycle for combination of all Outfits` is what my question is asking. I cannot come up with a robust algorithm for it. – Hatefiend Jul 03 '17 at 13:15
  • You have set NAMES for them. Either they are enums and have names or they are not and they haven't. Again I am repeating - you do not understand your own data structure and really you haven't any to speak about. – Gangnus Jul 03 '17 at 13:19
-2

for an enum , you have to use the method "values()" to loop on it:

 For (clothe c: clothes.values())
Greg Artisi
  • 172
  • 2
  • 12
  • 1
    I know this but unfortunately that doesn't help me with this question. – Hatefiend Jul 03 '17 at 09:08
  • You Can try the fork/join framework that allow you to create recursive tasks ans dividing thé work into different threads – Greg Artisi Jul 03 '17 at 09:10
  • I will do that for extra performance but right now I cannot solve an algorithm that actually loops over every combination. – Hatefiend Jul 03 '17 at 09:15
  • When the person wears i, then She cannot Wear thé same h.when thé person Wear à h, then She cannot Wear thé same i. Maybe you should unwear thé person everytime you loop on i. – Greg Artisi Jul 03 '17 at 09:22
  • On thé first loop, She will Wear à red vest, but you dont have a p.wear(i) in thé first outside loop, so She Wear only one clothe, is it normal ? – Greg Artisi Jul 03 '17 at 09:33