JAVA - BFS to a limited friendship level in a graph - java

I am working on an undirected graph implementation for a simple social network. Users are represented using their IDs (Integers), and I need to find different levels of friendship.
I used the Adjacency List approach as my graph is very sparse. I used a hashmap to hold users and their friends:
Map<Integer, Set<Integer>> graph;
Using this implementation, I am able to find the first and second levels of friendship. However, I wrote a method that uses BFS to find if two users are fourth-level friends.
For example, if the graph contains following edges:
1-2
2-3
3-4
4-5
Then 1 and 5 are fourth-level friends, and my method should return true when 1 and 5 are passed to it as parameters.
The problem is that my method always returns false when it is called in the main, even if the method itself is tested and is correct! Here is the method, and again it is correct and working.
public boolean checkLevelBFS(Integer source, Integer dest) {
Queue<Integer> toVisitQueue = new LinkedList<>(graph.get(source));
Set<Integer> visited = new HashSet<>();
visited.add(source);
Integer inreaser = new Integer(-1); // indicator for level increase
toVisitQueue.add(inreaser);
int level = 1; // because we already passed the source and started from its children nodes
while (level <= 4 && !toVisitQueue.isEmpty()) {
Integer currentNode = toVisitQueue.remove();
if (currentNode.equals(dest)) {
return true;
}
if (visited.contains(currentNode)) {
continue;
}
if (currentNode.equals(inreaser)) {
level++;
toVisitQueue.add(inreaser);
continue;
}
visited.add(currentNode);
for (Integer child : graph.get(currentNode)) {
if (!visited.contains(child)){
toVisitQueue.add(child);
}
}
}
return false;
}
The part that makes the code return false is below:
public static void main(String[] args) {
Basics x = new Basics();
x.graph = new HashMap<>();
Set<Integer> s = new HashSet<>();
s.add(2);
x.graph.put(1, s);
s = new HashSet<>();
s.add(1);
s.add(3);
x.graph.put(2, s);
s = new HashSet<>();
s.add(2);
s.add(4);
x.graph.put(3, s);
s = new HashSet<>();
s.add(3);
s.add(5);
x.graph.put(4, s);
s = new HashSet<>();
s.add(4);
s.add(6);
x.graph.put(5, s);
s = new HashSet<>();
s.add(5);
x.graph.put(6, s);
if (!x.initialCheck(1, 5)) {
System.out.println("A new user is involved");
} else {
if (x.levelOneFriends(1, 5)) {
System.out.println("friends");
} else {
System.out.println("not friends");
if (x.levelTwoFriends(1, 5)) {
System.out.println("friends level 2");
} else {
System.out.println("not friends of level 2");
if (x.checkLevelBFS(1, 5)) {
System.out.println("YES - friends of level 4");
} else {
System.out.println("NO - not friends of level 4");
}
}
}
}
System.out.println(x.checkLevelBFS(1, 2));
System.out.println(x.checkLevelBFS(1, 3));
System.out.println(x.checkLevelBFS(1, 4));
System.out.println(x.checkLevelBFS(1, 5));
System.out.println(x.checkLevelBFS(1, 6));
}
Output:
not friends
not friends of level 2
NO - not friends of level 4
false
false
false
false
false
The first two lines are correct outputs, the third is not correct as 1 and 5 are frieds of level 4, and should print YES -
The following 'false' outputs are weird too!
'initialCheck' checks if any of the two users is not already in the graph.
'levelOneFriends' checks if the two objects are direct friends.
'levelTwoFriends' checks if the two objects are in the relation Friend of Friend.
Any help?
Thanks!

[Edited after OP's comment showing levelTwoFriends]
public boolean levelTwoFriends(Integer user1, Integer user2) {
Collection<Integer> c = graph.get(user1);
// c.retainAll(graph.get(user2));
// return !c.isEmpty();
// true only if a non-void intersection
return false==Collections.disjoint(c, graph.get(user2));
}
or, as a one-liner
public boolean levelTwoFriends(Integer user1, Integer user2) {
return false==Collections.disjoint( graph.get(user1), graph.get(user2) );
}
[Edited after update with x.initialCheck]
Your bug then must be in the x.initialCheck and the other stuff that you are doing before actually getting into x.checkLevelBFS. Verify the x.initialCheck and x.levelOneFriends against modification in the internal representation of you adjacency list (there must be one there - run the in debugger and keep an eye on modification of the content). Why I say this? Because the below code works as intended:
static public void main(String[] args) {
ExampleTest x=new ExampleTest(); // in your case, it's Basics
x.graph=new HashMap<>();
x.graph.put(1, new HashSet<>(Arrays.asList(2)));
x.graph.put(2, new HashSet<>(Arrays.asList(1,3)));
x.graph.put(3, new HashSet<>(Arrays.asList(2,4)));
x.graph.put(4, new HashSet<>(Arrays.asList(3,5)));
x.graph.put(5, new HashSet<>(Arrays.asList(4,6)));
x.graph.put(6, new HashSet<>(Arrays.asList(5)));
System.out.println(x.checkLevelBFS(1, 2)); // true
System.out.println(x.checkLevelBFS(1, 3)); // true
System.out.println(x.checkLevelBFS(1, 4)); // true
System.out.println(x.checkLevelBFS(1, 5)); // true
System.out.println(x.checkLevelBFS(1, 6)); // false
}
"The problem is that my method always returns false!"
But it doesn't! Your bug may be in some other place!!!
I'll be marking this as Works for me and closing the bug.
My code used for driving the tests.
class ExampleTest {
Map<Integer, Set<Integer>> graph;
static public void main(String[] args) {
ExampleTest x=new ExampleTest();
x.graph=new HashMap<>();
x.graph.put(1, new HashSet<>(Arrays.asList(2)));
x.graph.put(2, new HashSet<>(Arrays.asList(3)));
x.graph.put(3, new HashSet<>(Arrays.asList(4)));
x.graph.put(4, new HashSet<>(Arrays.asList(5)));
x.graph.put(5, new HashSet<>(Arrays.asList(6)));
System.out.println(x.checkLevelBFS(1, 2)); // true
System.out.println(x.checkLevelBFS(1, 3)); // true
System.out.println(x.checkLevelBFS(1, 4)); // true
System.out.println(x.checkLevelBFS(1, 5)); // true
System.out.println(x.checkLevelBFS(1, 6)); // false
}
// verbatim copy of your method here
public boolean checkLevelBFS(Integer source, Integer dest) {
Queue<Integer> toVisitQueue = new LinkedList<>(graph.get(source));
Set<Integer> visited = new HashSet<>();
visited.add(source);
Integer inreaser = new Integer(-1); // indicator for level increase
toVisitQueue.add(inreaser);
int level = 1; // because we already passed the source and started from its children nodes
while (level <= 4 && !toVisitQueue.isEmpty()) {
Integer currentNode = toVisitQueue.remove();
if (currentNode.equals(dest)) {
return true;
}
if (visited.contains(currentNode)) {
continue;
}
if (currentNode.equals(inreaser)) {
level++;
toVisitQueue.add(inreaser);
continue;
}
visited.add(currentNode);
for (Integer child : graph.get(currentNode)) {
if (!visited.contains(child)){
toVisitQueue.add(child);
}
}
}
return false;
}
}

Related

How to do a lotto program without using loops and array statements?

Our professor tasked us to create a lotto program that will generate 6 random numbers ranging from 1-55 without us using the arrays and loops, we can only use "if else" statements. We also have to put scanner in which the user/better will input his 6 numbers also ranging from 1-55. The user's numbers should win as long as the 6 of the numbers match regardless of arrangements. For example
User: 1,2,3,4,5,6
Random Number:6,3,4,2,1,5
System: You Win!
I created a lotto program but my program requires every numbers to match with regards to their arrangement
User: 1,2,3,4,5,6
Random:6,5,4,3,2,1
System: You Lose
I don't have any idea how to ignore the arrangements. I tried search for a lot of thing in the internet and they mostly use arrays and loops.
public class LottoGame {
public static void main(String... args) {
Scanner scan = new Scanner(System.in);
System.out.print("Input 6 numbers [1-55] separated with space: ");
NumberSet userNumberSet = new NumberSet();
userNumberSet.a = new AtomicInteger(scan.nextInt());
userNumberSet.b = new AtomicInteger(scan.nextInt());
userNumberSet.c = new AtomicInteger(scan.nextInt());
userNumberSet.d = new AtomicInteger(scan.nextInt());
userNumberSet.e = new AtomicInteger(scan.nextInt());
userNumberSet.f = new AtomicInteger(scan.nextInt());
System.out.format("User: %d,%d,%d,%d,%d,%d\n", userNumberSet.a.get(), userNumberSet.b.get(),
userNumberSet.c.get(), userNumberSet.d.get(), userNumberSet.e.get(), userNumberSet.f.get());
userNumberSet.sort();
NumberSet randomNumberSet = getRandomNumberSet();
System.out.format("Randome Number: %d,%d,%d,%d,%d,%d\n", randomNumberSet.a.get(), randomNumberSet.b.get(),
randomNumberSet.c.get(), randomNumberSet.d.get(), randomNumberSet.e.get(),
randomNumberSet.f.get());
randomNumberSet.sort();
boolean win = userNumberSet.a.get() == randomNumberSet.a.get();
win &= userNumberSet.b.get() == randomNumberSet.b.get();
win &= userNumberSet.c.get() == randomNumberSet.c.get();
win &= userNumberSet.d.get() == randomNumberSet.d.get();
win &= userNumberSet.e.get() == randomNumberSet.e.get();
win &= userNumberSet.f.get() == randomNumberSet.f.get();
System.out.println("System: " + (win ? "You Win!" : "You Lose"));
}
private static NumberSet getRandomNumberSet() {
Random random = new Random();
NumberSet numberSet = new NumberSet();
numberSet.a = new AtomicInteger(random.nextInt(55) + 1);
numberSet.b = new AtomicInteger(random.nextInt(55) + 1);
numberSet.c = new AtomicInteger(random.nextInt(55) + 1);
numberSet.d = new AtomicInteger(random.nextInt(55) + 1);
numberSet.e = new AtomicInteger(random.nextInt(55) + 1);
numberSet.f = new AtomicInteger(random.nextInt(55) + 1);
return numberSet;
}
private static final class NumberSet {
private AtomicInteger a;
private AtomicInteger b;
private AtomicInteger c;
private AtomicInteger d;
private AtomicInteger e;
private AtomicInteger f;
public void sort() {
sort(a, b);
sort(b, c);
sort(c, d);
sort(d, e);
sort(e, f);
}
private void sort(AtomicInteger one, AtomicInteger two) {
if (one.get() > two.get()) {
int tmp = one.get();
one.set(two.get());
two.set(tmp);
}
if (two == b) {
sort(one, c);
} else if (two == c) {
sort(one, d);
} else if (two == d) {
sort(one, e);
} else if (two == e) {
sort(one, f);
}
}
}
}
P.S. I think you are able to update a bit this snippte to check that NumberSet contains only unique numbers.

Need feedback on my Breadth First Search algorithm between two strings

I am attempting to find the shortest path in a route between two points. My adjacency list seems to be correct but the breadth-first search seems to be messing up somewhere. If no path exists between two points in the graph, "Not found" is printed. My code never seems to enter this for some reason, even if it should. My basic understanding of the BST algorithm is making this problem extremely hard to diagnose. I've spent countless hours modifying the code and watching videos but have remained unsuccessful.
I am reading the route data from a text. This part is working perfectly, therefore I feel like it would be redundant to include. What I will say is that the adjacency list my graph code creates looks correct, so it's likely an issue with my BFS function.
class Graph {
// We use Hashmap to store the edges in the graph
private Map<String, List<String> > map = new HashMap<>();
public void BFS(String start, String stop) {
Queue<String> queue = new ArrayDeque<>();
HashSet<String> seen = new HashSet<>();
ArrayList<String> network = new ArrayList<>();
queue.add(start);
while(0 != queue.size()){
String vertex = queue.poll();
if(!seen.contains(vertex)){
network.add(vertex);
queue.addAll(map.get(vertex)); // Add all neighbors of 'vertex' to the queue
seen.add(vertex);
}
}
if (network.contains(stop)) {
System.out.println("Route Path: ");
for(String location: network) {
if (location.equals(stop)) {
System.out.println(location);
break;
} else {
System.out.println(location + " -> ");
}
}
} else {
System.out.println("Not found.");
}
}
public void printMap() {
for(String item: map.keySet()) {
System.out.println(map.get(item));
}
}
// This function adds a new vertex to the graph
public void addVertex(String s)
{
map.put(s, new LinkedList<String>());
}
// This function adds the edge
// between source to destination
public void addEdge(String source,
String destination,
boolean bidirectional)
{
if (!map.containsKey(source))
addVertex(source);
if (!map.containsKey(destination))
addVertex(destination);
map.get(source).add(destination);
if (bidirectional == true) {
map.get(destination).add(source);
}
}
// This function gives the count of vertices
public void getVertexCount()
{
System.out.println("The graph has "
+ map.keySet().size()
+ " vertex");
}
// This function gives the count of edges
public void getEdgesCount(boolean bidirection)
{
int count = 0;
for (String v : map.keySet()) {
count += map.get(v).size();
}
if (bidirection == true) {
count = count / 2;
}
System.out.println("The graph has "
+ count
+ " edges.");
}
// This function gives whether
// a vertex is present or not.
public boolean hasVertex(String s)
{
if (map.containsKey(s)) {
return true;
}
else {
return false;
}
}
// This function gives whether an edge is present or not.
public boolean hasEdge(String s, String d)
{
if (map.get(s).contains(d)) {
return true;
}
else {
return false;
}
}
// Prints the adjancency list of each vertex.
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
for (String v : map.keySet()) {
builder.append(v.toString() + ": ");
for (String w : map.get(v)) {
builder.append(w.toString() + " ");
}
builder.append("\n");
}
return (builder.toString());
}
If my question is lacking in any way, please provide constructive feedback so I can make better posts in the future. I can refine my post and supply further information if needed.

I am not able to add an element to my array list using ".add" operator in Java (Polynomial function)

I'm very new to coding so still struggling and having a bit of problem with my code. For this code, I'm supposed to create a polynomial function using ArrayList by adding polyterm with parameters c (coefficient) and e (exponent) to the list. I was able to construct the code; however, whenever I try running the test code it always says that my polyterm was not added to the list. I wonder if one of you guys could kindly assist me?
Here are my codes.
package polynomials;
import java.util.ArrayList;
public class Polynomial {
public ArrayList<PolyTerm> terms;
/**
* initialize terms to be an empty list
*/
public Polynomial() {
//to be completed
terms = new ArrayList<PolyTerm>();
}
/**
* add a term with the given coefficient and exponent to the list.
* if a term with given exponent already exists, its coefficient should be updated accordingly.
* #param c (coefficient)
* #param e (exponent)
*/
public void addTermBasic(double c, int e) {
//to be completed
PolyTerm p = new PolyTerm(c, e);
for (int i = 0; i < terms.size(); i++) {
if(p.exponent == terms.get(i).exponent) {
if (terms.get(i).coefficient + p.coefficient == 0) {
terms.remove(i);
} else {
terms.get(i).coefficient = terms.get(i).coefficient + p.coefficient;
}
} else {
terms.add(new PolyTerm(p.coefficient, p.exponent));
}
}
}
}
and here are the text code
package polynomials;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PolynomialTest {
public static int score = 0;
public static String result = "";
public static String currentMethodName = null;
ArrayList<String> methodsPassed = new ArrayList<String>();
Polynomial p1, p2, p3, p4;
#BeforeEach
public void setUp() throws Exception {
currentMethodName = null;
p1 = new Polynomial();
p1.addTermBasic(new PolyTerm(1, 2));
p1.addTermBasic(new PolyTerm(-2, 1));
p1.addTermBasic(new PolyTerm(1, 0));
//p1 represents x^2 - 2x + 1
p2 = new Polynomial();
p2.addTermBasic(new PolyTerm(-2, 5));
p2.addTermBasic(new PolyTerm(5, 3));
p2.addTermBasic(new PolyTerm(1, 2));
//p2 represents -2x^5 + 5x^3 + x^2
p3 = new Polynomial();
//p3 represents an empty polynomial
p4 = new Polynomial();
p4.addTermBasic(new PolyTerm(6, 4));
//p4 represents 6x^4
}
#Test #Order(1) #Graded(description="Polynomial()", marks=2)
public void testPolynomial() {
assertNotNull(p3);
assertNotNull(p3.terms);
assertEquals(0, p3.terms.size());
currentMethodName = new Throwable().getStackTrace()[0].getMethodName();
}
#Test #Order(2) #Graded(description="addTermBasic(double, int)", marks=20)
public void testAddTermBasicDoubleInt() {
assertNotNull(p1.terms);
**assertEquals(3, p1.terms.size());**
assertEquals(1, p1.terms.get(0).coefficient, 0.001);
assertEquals(2, p1.terms.get(0).exponent);
assertEquals(-2, p1.terms.get(1).coefficient, 0.001);
assertEquals(1, p1.terms.get(1).exponent);
assertEquals(1, p1.terms.get(2).coefficient, 0.001);
assertEquals(0, p1.terms.get(2).exponent);
p1.addTermBasic(1.5, 2);
assertEquals(3, p1.terms.size());
assertEquals(2.5, p1.terms.get(0).coefficient, 0.001);
assertEquals(2, p1.terms.get(0).exponent);
p1.addTermBasic(-5, 0);
assertEquals(3, p1.terms.size());
assertEquals(-4, p1.terms.get(2).coefficient, 0.001);
assertEquals(0, p1.terms.get(2).exponent);
p1.addTermBasic(4, 0); //adding 4x^0 to -4x^0
assertEquals(2, p1.terms.size());
assertEquals(2.5, p1.terms.get(0).coefficient, 0.001);
assertEquals(2, p1.terms.get(0).exponent);
assertEquals(-2, p1.terms.get(1).coefficient, 0.001);
assertEquals(1, p1.terms.get(1).exponent);
p1.addTermBasic(-1.2, 6);
assertEquals(3, p1.terms.size());
assertEquals(-1.2, p1.terms.get(2).coefficient, 0.001);
assertEquals(6, p1.terms.get(2).exponent);
p2.addTermBasic(1, 4);
assertEquals(4, p2.terms.size());
assertEquals(1, p2.terms.get(3).coefficient, 0.001);
assertEquals(4, p2.terms.get(3).exponent);
p2.addTermBasic(-1, 4);
assertEquals(3, p2.terms.size());
assertEquals(1, p2.terms.get(2).coefficient, 0.001);
assertEquals(2, p2.terms.get(2).exponent);
p2.addTermBasic(0, 8);
assertEquals(3, p2.terms.size());
assertEquals(1, p2.terms.get(2).coefficient, 0.001);
assertEquals(2, p2.terms.get(2).exponent);
currentMethodName = new Throwable().getStackTrace()[0].getMethodName();
}
}
The error that I always receive is AssertionFailedError: exected <3> but was: <0> at the bolded line in the test code line 54
I've tried both of the suggestion; however, the error is still the same. Therefore, I tried to use one single .add() to see whether or not any polyterm get added but I still ended up with an empty list.
public void addTermBasic(double c, int e) {
//to be completed
PolyTerm p = new PolyTerm(c, e);
terms.add(p);
}
Your logic is flawed.
for (int i = 0; i < terms.size(); i++) {
if(terms.get(i)...) {
...
} else {
terms.add(...);
}
}
If the list is initially empty, this loop will not do anything, and the else clause will also not occur, so no terms ever get added.
A for loop does not have an else clause, but you can use a boolean to fix it:
boolean found = false;
for (int i = 0; i < terms.size(); i++) {
if(terms.get(i)...) {
...
found = true;
}
}
if (!found) {
terms.add(...);
}
Learn to debug. Step through the code (with a debugger, or if you must, with a lot of System.out statements) using some simple-ish but realistic inputs, and check what the computer does, vs. what you think it should do - use pen and paper if you must. There where you and computer disagree? You found a bug.
HINT: You call .add() within the for loop of addTermBasic. That's not right.

Java Detecting a cyclic directed Graph

I am currently trying to write a procedure to check whether a directed graph is cyclic or not. I am not sure what i did wrong (it may be well possible that I did everything wrong, so please StackOverflow, show me my stupidity!). I'd be thankful for any kind of help as I've come to the point where I don't know what could be the problem.
The input is an adjacency list such as:
0: 2 4
1: 2 4
2: 3 4
3: 4
4: 0 1 2 3
(0 directs to 2 and 4; 1 directs to 2 and 4 and so on...)
The idea is that I check whether the node I am checking is 'grey' (partially explored) or not. If it is, it must be a back edge and thus a cyclic graph. Black edges are always explored or cross-edges, so this shouldn't trigger a cyclic message. I am aiming to do depth first search
If A-->B and B-->A, this should not trigger a message about cyclic (but A--> B, B-->C, C-->A should).
hasCycle calls hasCycleInSubgraph which calls itself recursively through the Adjency List of the Graph.
class qs {
private ArrayList<Integer>[] adjList;
private Stack<Integer> stack;
private ArrayList<Integer> whiteHat;
private ArrayList<Integer> greyHat;
private ArrayList<Integer> blackHat;
public qs(ArrayList<Integer>[] graph) {
this.adjList = graph;
this.stack = new Stack();
this.whiteHat = new ArrayList<Integer>();
this.greyHat = new ArrayList<Integer>();
this.blackHat = new ArrayList<Integer>();
for (Integer h = 0; h < adjList.length; h++) {
whiteHat.add(h);
}
}
public boolean hasCycle() {
for (Integer i = 0; i < adjList.length; i++) {
// System.out.print("Local root is: ");
// System.out.println(i);
whiteHat.remove(i);
greyHat.add(i);
if (hasCycleInSubgraph(i) == true) {
return true;
}
greyHat.remove(i);
blackHat.add(i);
}
return false;
}
public boolean hasCycleInSubgraph(Integer inp) {
if (blackHat.contains(inp)) {
return false;
}
for (Integer branch : adjList[inp]) {
// System.out.print("Adj is: ");
// System.out.println(branch);
if ( greyHat.contains(branch) && !inp.equals(branch) ) {
return true;
}
whiteHat.remove(branch);
greyHat.add(branch);
if ( hasCycleInSubgraph(branch) == true ) {
return true;
}
greyHat.remove(branch);
blackHat.add(branch);
}
return false;
}
}
You are over-complicating it: a cycle can be detected via a depth-first search: from any given node, walk to each of the connected nodes; if you arrive back at an already-visited node, you've got a cycle.
class qs {
private final ArrayList<Integer>[] graph;
qs(ArrayList<Integer>[] graph) {
this.graph = graph;
}
boolean hasCycle() {
List<Integer> visited = new ArrayList<>();
for (int i = 0; i < graph.length; ++i) {
if (hasCycle(i, visited)) {
return true;
}
}
}
private boolean hasCycle(int node, List<Integer> visited) {
if (visited.contains(node)) {
return true;
}
visited.add(node);
for (Integer nextNode : graph[node]) {
if (hasCycle(nextNode, visited)) {
return true;
}
}
visited.remove(visited.length() - 1);
return false;
}
}
If you want to detect cycles longer than a given length, just check the depth of the recursion:
if (visited.contains(node) && visited.size() > 2) {
Note that this does not require any state to be kept, aside from what is in the stack. Relying upon mutable state makes the code thread-unsafe (e.g. that two threads calling hasCycle at the same time would interfer with each other), and so should be avoided - even if you don't expect the code to be used in a multi-threaded way now, it avoids problems down the line.

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;
}
}
Actually this can be simplified quite a lot.
So let's take a look at your rules for what are matches:
{Course1Score} is minimal
{Course2Score} ...
{Course3Score} ...
{Course1Score, Course2Score} optimal or dominant solution
{Course1Score, Course3Score} ...
{Course2Score, Course3Score} ...
{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.

Categories