tubby
tubby

Reputation: 2144

Finding smallest triplets given a set of options

I am working on this problem to find the smallest triplet given a set of triplets which is smallest in all three dimensions. However, if there exists a triplet which is smallest is any one or two dimensions than the smallest across all three dimensions, that too needs to be considered.

Example

Let me give an example. Say, my triplets are as follows

(25, 30, 34), (15, 31, 21), (10, 40, 21), (50, 30, 34), (25, 30, 10), (9, 20, 15)

Now, (9,20,15) looks to be the smallest across all three dimensions. However, there also exists an option (25,30,10) which is smallest in the third dimension since it's score in the third diemnsion(10) is smallest than any of the other options, so that too needs to be included in the output too. so the final output would be {(9,20,15) and (25,30,10)}

So, basically a solution should be considered if it is either Dominant or Optimal. I can define these as follows

1. Dominant : If an outcome o is at least as good for another agent as another outcome o' and there is some
agent who strictly prefers o to o'. Then o pareto-dominates o'

2. Optimal : o* is pareto-optimal if it isn't pareto-dominated by anything else

My algorithm

This is my algorithm. Please see the attached code. Let the three dimensions be called Course1Score, Course2Score and Course3Score.

So I traverse across the inputs, and find the smallest for each of the following

1.{Course1Score}
2.{Course2Score}
3.{Course3Score}
4.{Course1Score, Course2Score}
5.{Course1Score, Course3Score}
6.{Course2Score, Course3Score}
7.{Course1Score, Course2Score, Course3Score}

I then find the distinct solution among the solutions which minimize the above objectives.

False case

Now this would work for most cases except in a case like below. Let the input be (1,100,100), (100,1,100), (100,100,1), (1,1,1) As per the above algorithm, the scores which minimize the above objectives would be as follows

 1.Minimize {Course1Score} - (1,100,100) 
(remember this won't be over-written by say,(1,1,1) since Course1Score of (1,1,1) IS NOT LESS than Course1Score of (1,100,100) which comes first in the order of inputs) )
    2.Minimize {Course2Score} - (100,1,100)
    3.Minimize {Course3Score} - (100,100,1)
    4.Minimize {Course1Score, Course2Score} - (1,100,100)
    5.Minimize {Course1Score, Course3Score} - (1,100,100)
    6.Minimize {Course2Score, Course3Score} - (100,1,100)
    7.Minimize {Course1Score, Course2Score, Course3Score} - (1,1,1)

Continuing with the algorithm, we find the distinct solution and report them as (1,100,100), (100,1,100), (100,100,1), (1,1,1) However, this solution is incorrect. Since (1,1,1) beats all of the other solutions in all three dimensions, and none of (1,100,100), (100,1,100), (100,100,1) are better than (1,1,1) in any of the dimensions. So, I add an extra condition to check this at the end which can be found in the function

Online java fiddle

This is an online java fiddle of my implementation

My implementation

Please find my code

import java.util.ArrayList;

/* Glossary
 * 1. Dominant : If an outcome o is at least as good for another agent as another outcome o' and there is some
 * agent who strictly prefers o to o'. Then o Triplet-dominates o'
 * 
 * 2. Optimal : o* is Triplet-optimal if it isn't Triplet-dominated by anything else
 * 
 *   */
public class HelloWorld
{
    public static void main(String[] args)
    {
        Triplet myTriplet = new Triplet();

        /* Populating input and printing them */
        System.out.println("Printing input");
        myTriplet.PopulateSampleInput();
        myTriplet.Print(myTriplet.options);

        /* Printing the Triplet-Optimal solutions */
        ArrayList<Option> TripletSolutions = myTriplet.FindTripletOptimalSolutions();
        System.out.println("Printing TripletSolutions : ");
        myTriplet.Print(TripletSolutions);
    }
}

class Triplet
{
    ArrayList<Option> options;

    public Triplet()
    {
        options = new ArrayList<Option>();
    }

    void PopulateSampleInput()
    {
        Option option1 = new Option(25, 30, 34);
        Option option2 = new Option(15, 31, 21);
        Option option3 = new Option(10, 40, 21);
        Option option4 = new Option(50, 30, 34);
        Option option5 = new Option(25, 30, 10);
        Option option6 = new Option(9, 20, 15);

        options.add(option1);
        options.add(option2);
        options.add(option3);
        options.add(option4);
        options.add(option5);
        options.add(option6);
    }

    void Print(ArrayList<Option> al)
    {
        for(int i = 0;i< al.size();i++)
        {
            System.out.println(al.get(i).Course1Score + "," + al.get(i).Course2Score + "," + al.get(i).Course3Score);
        }
    }

    ArrayList<Option> FindTripletOptimalSolutions()
    {
        Option[] map = new Option[7];

        /* Initialization : Initially the best solution for minimizing all objectives is the first solution */
        for(int i = 0;i<map.length;i++)
            map[i] = options.get(0);

        for(int i=1;i<options.size();i++)
        {
            /* Fixing {1} */
            if(options.get(i).Course1Score < map[0].Course1Score)
                map[0] = options.get(i);

            /* Fixing {2} */
            if(options.get(i).Course2Score < map[1].Course2Score)
                map[1] = options.get(i);

            /* Fixing {3} */
            if(options.get(i).Course3Score < map[2].Course3Score)
                map[2] = options.get(i);

            /* Fixing {1,2} */
            if(options.get(i).Course1Score <= map[3].Course1Score && options.get(i).Course2Score <= map[3].Course2Score)
                map[3] = options.get(i);

            /* Fixing {1,3} */
            if(options.get(i).Course1Score <= map[4].Course1Score && options.get(i).Course3Score <= map[4].Course3Score)
                map[4] = options.get(i);

            /* Fixing {2,3} */
            if(options.get(i).Course2Score <= map[5].Course2Score && options.get(i).Course3Score <= map[5].Course3Score)
                map[5] = options.get(i);

            /* Fixing {1,2,3} */
            if(options.get(i).Course1Score <= map[6].Course1Score && options.get(i).Course2Score <= map[6].Course2Score && options.get(i).Course3Score <= map[6].Course3Score)
                map[6] = options.get(i);
        }

        /* find unique solutions */
        ArrayList<Option> DistinctSolutions = new ArrayList<Option>();
        DistinctSolutions = findUnique(map); 

        /* keeping only solutions that add something new */
        ArrayList<Option> TripletSolutions = EliminateWeakSolutionInCaseOfTie(DistinctSolutions);
        return TripletSolutions;
    }

    /* This function returns the unique solutions, otherwise, they will cancel out each other inside the 
     * EliminateWeakSolutionInCaseOfTie function that comes next */
    ArrayList<Option> findUnique(Option[] map)
    {
        ArrayList<Option> TripletSolutions = new ArrayList<Option>();
        for(int i = 0;i<map.length;i++)
        {
            if(!TripletSolutions.contains(map[i]))
                TripletSolutions.add(map[i]);
        }
        return TripletSolutions;
    }

    /* This function in case of ties where map[0]'s Course1Score is only equal to, but not less than
     * map[6]'s Course1Score, which in addition to minimizing Course1Score, also minimizes
     * Course2Score and Course3Score */
    ArrayList<Option> EliminateWeakSolutionInCaseOfTie(ArrayList<Option> DistinctSolutions)
    {
        ArrayList<Option> TripletSolutions = new ArrayList<Option>();
        int Include = 1;
        for(int i = 0;i<DistinctSolutions.size();i++,Include=1)
        {
            for(int j = 0;j<DistinctSolutions.size();j++)
            {
                if(i!=j && DistinctSolutions.get(j).Course1Score <= DistinctSolutions.get(i).Course1Score && DistinctSolutions.get(j).Course2Score <= DistinctSolutions.get(i).Course2Score && DistinctSolutions.get(j).Course3Score <= DistinctSolutions.get(i).Course3Score)
                {
                    Include = 0;
                    break;
                }
            }
            if(Include == 1)
                TripletSolutions.add(DistinctSolutions.get(i));
        }
        return TripletSolutions;
    }
}

class Option
{
    int Course1Score;
    int Course2Score;
    int Course3Score;

    public Option(int Course1Score, int Course2Score, int Course3Score)
    {
        // TODO Auto-generated constructor stub
        this.Course1Score = Course1Score;
        this.Course2Score = Course2Score;
        this.Course3Score = Course3Score;
    }
}

Can you please suggest an algorithm for the above, and/or review my algo and implementation?

EDIT : I think this solution works

Pseudocode

ParetoSolutionPool[1] = input[1]

for(i in 2:input)
    boolean ParetoDominant = false;
    boolean ParetoOptimal = true;

    for(j in 1:ParetoSolutionPool)
        if(input[i] ParetoDominates ParetoSolutionPool[j])
            remove ParetoSolutionPool[j]
            ParetoDominant = true;

        if(input[i] IsParetoDominatedBy ParetoSolutionPool[j])//extra if(ParetoDominant == false && ParetoOptimal == true && above)
            ParetoOptimal = false;
    end of for loop over j

    if(ParetoDominant || ParetoOptimal == true)
        add input[i] to ParetoSolutionPool

end of for loop over i

Pseudocode in words

Basically, two checks. 1.If an input/option domoinates(lower in all three dimensions), one of the existing solutions, that "existing solution" is popped, and replaced by this input, since it's better unanimously. (Eg 25,30,10 better than 25,30,34)

2.If an input is NOT WORSE (in all three dimensions )than any of the existing solutions, then it too has to be considered to the solution pool.

So basically in either of the two cases, above, an input is added to the solution pool. Only difference between the two being that in the first case, the weaker existing solution is popped as well.

Code

package TripletOptimizationAlgorithms;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;

public class algo4
{
    public static void main(String[] args)
    {
        Triplet myTriplet = new Triplet();

        /* Populating input and printing them */
        System.out.println("Printing input");
        //myTriplet.PopulateSampleInput();
        myTriplet.PopulateSampleInputFromFile();
        myTriplet.Print(myTriplet.options);
        System.out.println("Number of inputs read=="+myTriplet.options.size());

        /* Printing the Triplet-Optimal solutions */
        final long startTime = System.currentTimeMillis();
        ArrayList<Option> TripletSolutions = myTriplet.FindTripletOptimalSolutions();
        final long endTime = System.currentTimeMillis();

        System.out.println("Printing TripletSolutions : ");
        myTriplet.Print(TripletSolutions);

        System.out.println("Total execution time: " + (endTime - startTime) + " milliseconds" );
    }
}

class Triplet
{
    ArrayList<Option> options;

    public Triplet()
    {
        options = new ArrayList<Option>();
    }

    void PopulateSampleInput()
    {
        Option option1 = new Option(25, 30, 34);
        Option option2 = new Option(15, 31, 21);
        Option option3 = new Option(10, 40, 21);
        Option option4 = new Option(30, 30, 34);
        Option option5 = new Option(25, 30, 10);
        Option option6 = new Option(9, 20, 15);

        options.add(option1);
        options.add(option2);
        options.add(option3);
        options.add(option4);
        options.add(option5);
        options.add(option6);
    }

    void PopulateSampleInputFromFile()
    {
        try
        {
            String pwd = Paths.get(".").toAbsolutePath().normalize().toString();
            String inputpath = pwd + "/src/TripletOptimizationAlgorithms/myinput.txt";

            FileInputStream fstream = new FileInputStream(inputpath);
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            while ((strLine = br.readLine()) != null)
            {
                String[] tokens = strLine.split(" ");
                Option myoption = new Option(Integer.parseInt(tokens[0]),Integer.parseInt(tokens[1]),Integer.parseInt(tokens[2]));//process record , etc
                options.add(myoption);
            }
            in.close();
        }
        catch (Exception e)
        {
            System.err.println("Error: " + e.getMessage());
        }
    }

    void Print(ArrayList<Option> al)
    {
        for(int i = 0;i< al.size();i++)
        {
            System.out.println(al.get(i).Course1Score + "," + al.get(i).Course2Score + "," + al.get(i).Course3Score);
        }
    }

    ArrayList<Option> FindTripletOptimalSolutions()
    {
        /* Initialization : Initialize the TripletSolutions to be the first option */
        ArrayList<Option> TripletSolutions = new ArrayList<Option>();
        TripletSolutions.add(options.get(0));

        /* looping across input */
        for(int i = 1;i<options.size();i++)
        {
            boolean TripletDominant = false;
            boolean TripletOptimal = true;
            Option optionUnderCheck = options.get(i);
            ArrayList<Integer> IndicesToRemove = new ArrayList<Integer>();

            /* looping across TripletSolutions */
            for(int j = 0;j<TripletSolutions.size();j++)
            {
                if(isTripletDominant(optionUnderCheck, TripletSolutions.get(j)) == true)
                {
                    TripletDominant = true;
                    IndicesToRemove.add(j);
                }

                if(IsTripletDominatedBy(optionUnderCheck, TripletSolutions.get(j)) == true)
                {
                    TripletOptimal = false;
                }
            }

            /* the weaker solutions have to be removed */
            if(TripletDominant == true)
            {
                Collections.sort(IndicesToRemove, Collections.reverseOrder());
                for(int k = 0;k<IndicesToRemove.size();k++)
                {
                    TripletSolutions.remove(IndicesToRemove.get(k).intValue());
                }
            }

            if(TripletDominant == true || TripletOptimal == true)
                TripletSolutions.add(optionUnderCheck);
        }

        return TripletSolutions;
    }

    boolean isTripletDominant(Option optionUnderCheck, Option existingSolution)
    {
        if(optionUnderCheck.Course1Score <= existingSolution.Course1Score && optionUnderCheck.Course2Score <= existingSolution.Course2Score && optionUnderCheck.Course3Score <= existingSolution.Course3Score)
            return true;
        return false;
    }

    boolean IsTripletDominatedBy(Option optionUnderCheck, Option existingSolution)
    {
        if(optionUnderCheck.Course1Score >= existingSolution.Course1Score && optionUnderCheck.Course2Score >= existingSolution.Course2Score && optionUnderCheck.Course3Score >= existingSolution.Course3Score)
            return true;
        return false;
    }
}

class Option
{
    int Course1Score;
    int Course2Score;
    int Course3Score;

    public Option(int Course1Score, int Course2Score, int Course3Score)
    {
        // TODO Auto-generated constructor stub
        this.Course1Score = Course1Score;
        this.Course2Score = Course2Score;
        this.Course3Score = Course3Score;
    }
}

Upvotes: 1

Views: 69

Answers (1)

user4668606
user4668606

Reputation:

Actually this can be simplified quite a lot.
So let's take a look at your rules for what are matches:

  1. {Course1Score} is minimal
  2. {Course2Score} ...
  3. {Course3Score} ...
  4. {Course1Score, Course2Score} optimal or dominant solution
  5. {Course1Score, Course3Score} ...
  6. {Course2Score, Course3Score} ...
  7. {Course1Score, Course2Score, Course3Score} ...

Rules 1,2 and 3 simply search for the value that minimizes CourseNScore (replace N with the rule-number). 4,5 and 6 search for pairs (a , b) (with a and b replaced by the respective CourseScore), such that there exists no pair with a lower a or b. These pairs will as well be found by Rules 1 - 3, no matter whether they are dominant or optimal. Same applies for rule 7 with rules 4 - 6.

Thus we can easily reduce the search to finding the tuples where 1 element is minimal and reduce the set of matches. This will speedup the search quite a bit. This will result in 3 Sets of tuples (one for each element of the tuples that is searched).

Next step: Reduce the set of found tuples. This can be done in a pretty naive approach:
A solution that matches Rule 7 must be in all sets generated by the search. A solution that matches Rule 4 must be in the sets that match Rule 1 and 2.

The reduction of the results thus becomes a pretty trivial task.

For the review:
Variable- and Method-names are usually lowercase in java.
The part of generating a String from Option, like used in Triplet.Print should be transferred to Option.toString(), since that's the usual way. But there's not much to criticize with this code.

Upvotes: 1

Related Questions