Simple weighted Graph: Loops not allowed exception - java

I'm attempting to create a programme which tracks the emails of people. I am using a string for the vertex in my graph (The string is their email) and a DefaultWeightedEdge from Jgrapht. If these people send one email between each other then the weight of the edge connecting that node is set to 1. If they send another email after already having sent one I increment the edge weight by 1.
I think I have the main bulk of the code correct, however I am getting this exception.
Exception in thread "main" java.lang.IllegalArgumentException: loops
not allowed at
org.jgrapht.graph.AbstractBaseGraph.addEdge(AbstractBaseGraph.java:203)
at groupProject.Analysis.StoreEmails(Analysis.java:58) at
groupProject.AnalyserRun.main(AnalyserRun.java:7)
Here is my code:
public class Analysis {
SimpleWeightedGraph<String, DefaultWeightedEdge> graph = new SimpleWeightedGraph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
jsonParser jP = new jsonParser("/Users/Kieran/test/test2.json");
int numEmails = jP.getNumEmails();
ArrayList<String> senders = new ArrayList<String>();
ArrayList<String> recipients = new ArrayList<String>();
ArrayList<String> all = senders;
ArrayList<DefaultWeightedEdge> edges = new ArrayList<DefaultWeightedEdge>();
public void StoreEmails(){
//Creates vertex's for every sender
for(int i = 0; i < numEmails; i++){
Email email = jP.parseJSON(i);
if(!senders.contains(email.getSender())){
graph.addVertex(email.getSender());
senders.add(email.getSender());
}
}
//creates vertex's for every recipient
for(int i = 0; i < numEmails; i++){
Email email = jP.parseJSON(i);
if(email.getRecipients().length != 0){
for(int j = 0; j < email.getRecipients().length; j++){
if(!recipients.contains(email.getRecipients()[j])){
graph.addVertex(email.getRecipients()[j]);
recipients.add(email.getRecipients()[j]);
}
}
}
}
all.removeAll(recipients);
all.addAll(recipients);
/*
* Adds all of the edges from senders to recipients and if the edge already exists then it will increase the weight by one
* however is is a directed graph so you need to check both pairs.
*/
for(int j = 0; j < numEmails; j++){
Email email = jP.parseJSON(j);
for(int k = 0; k < email.getRecipients().length; k++){
if(graph.containsEdge(email.getSender(), email.getRecipients()[k])){
int current_weight = (int) graph.getEdgeWeight(graph.getEdge(email.getSender(), email.getRecipients()[k]));
graph.setEdgeWeight(graph.getEdge(email.getSender(), email.getRecipients()[k]), current_weight+1);
}else{
DefaultWeightedEdge e = graph.addEdge(email.getSender(), email.getRecipients()[k]);
graph.setEdgeWeight(e, 1);
}
}
}
builder();
}
public int calcConnectedness(String s1,String s2){
int connectedness = 0;
int weightS1S2 = 0;
int weightS2S1 = 0;
if(graph.containsEdge(s1, s2)){
weightS1S2 = (int) graph.getEdgeWeight(graph.getEdge(s1, s2));
connectedness += weightS1S2;
}
/*if(graph.containsEdge(s2, s1)){
weightS2S1 = (int) graph.getEdgeWeight(graph.getEdge(s2, s1));
connectedness += weightS2S1;
}*/
return connectedness;
}
public void builder(){
for(int i = 0; i < all.size(); i++){
for(int j = i+1; j < all.size(); j++){
if(graph.containsEdge(all.get(i), all.get(j)))
make(all.get(i), all.get(j), calcConnectedness(all.get(i), all.get(j)));
}
}
}
public void make(String user1, String user2, int connectedness){
System.out.println(user1 + " " + user2 + " Are connected by a factor of: "+connectedness);
}
}
After some research the only information I was able to find which may be causing the problem is the fact that in Java, strings are immutable. However, I was still not able to resolve my issues.

The answer was in this section of code:
}else{
DefaultWeightedEdge e = graph.addEdge(email.getSender(), email.getRecipients()[k]);
graph.setEdgeWeight(e, 1);
}
It turns out that email.getSender() was in email.getRecipients() so the source and destination of the edge was the same i.e a loop. I solved the issue by doing a simple check beforehand with an if statement to only add an edge if it was not the same as the source.

Related

Java - outOfMemory - Heap Space

So I'm trying to complete an exercise where I've been asked to implement a method that does a binary search in an ArrayList of objects. From the exercise:
Binary search
In the Main-class, implement a method public static int binarySearch(ArrayList<Book> books, int searchedId), which searches the list it received as a parameter, for a book with an id variable that matches the value of searchedId variable it received as a parameter. If that book is found the method, should return the index it's located at, in the list it received as a parameter. If the book isn't found, the method should return the value -1.
The method must be implemented as a binary search, which assumes the list is ordered. You should also assume, that the ids towards the beginning of the list, are always smaller than the ids towards the end of the list.
I have created two methods, one to check whether the arraylist is sorted (isItSorted) and the other one that will perform the binary search if the aforementioned method evaluates to true (binarySearch). Please see below:
public static boolean isItSorted(ArrayList<Book> books) {
ArrayList<String> boo = new ArrayList<>();
String isItSorted = "";
for (int i = 0; i < books.size(); i++) {
for (int j = i + 1; j < books.size(); j++) {
if (books.get(i).getId() < books.get(j).getId()) {
isItSorted = "true";
boo.add(isItSorted);
} else {
isItSorted = "false";
boo.add(isItSorted);
}
}
}
if (!(boo.contains("false"))) {
return true;
}
return false;
}
public static int binarySearch(ArrayList<Book> books, long searchedId) {
if (searchedId < 0 || books.isEmpty()) {
return -1;
} else if (isItSorted(books)) {
int start = 0;
int end = books.size() - 1;
int middle = (start + end) / 2;
if (books.get(middle).getId() == searchedId) {
return middle;
} else if (books.get(middle).getId() > searchedId) {
end = middle - 1;
} else if (books.get(middle).getId() < searchedId) {
start = middle + 1;
}
while (start <= end) {
if (books.get(start).getId() == searchedId) {
return start;
}
start++;
}
}
return -1;
}
Inside these java files, there's a test package that tests whether my solution is correct or not. While 95% of the tests are successful, when it reaches the method below (where it compares the time of execution compared to my other method (linear search)), I get the error Java outOfMemory heap Space.
I use NetBeans. I've already tried the JVM commands.
My solution seems to work with every number of objects I've tried, so perhaps there's something wrong with the test code below?
#Test
#Points("07-05.2")
public void binarySearchIsFasterThanLinearSearch() throws Throwable {
ArrayList<Book> books = generateBooks(10000);
Collections.sort(books, (k1, k2) -> k1.getId() - k2.getId());
int searched = 1000001;
long bSearchStart = System.nanoTime();
int binarySearchId = Searching.binarySearch(books, searched);
long bSearchEnd = System.nanoTime();
assertTrue("When binary search does not find what was searched for, it must return -1", binarySearchId == -1);
long lSearchStart = System.nanoTime();
int linearSearchId = Searching.linearSearch(books, searched);
long lSearchEnd = System.nanoTime();
assertTrue("When linear search does not find what was searched for, it must return -1", linearSearchId == -1);
long bSearchTime = bSearchEnd - bSearchStart;
long lSearchTime = lSearchEnd - lSearchStart;
assertTrue("When there are 10000 books to search, and the searched book is not found, binary search should be a lot faster than linear search. Current this isn't so", bSearchTime * 2 < lSearchTime);
}
ArrayList<String> boo = new ArrayList<>();
String isItSorted = "";
for (int i = 0; i < books.size(); i++) {
for (int j = i + 1; j < books.size(); j++) {
if (books.get(i).getId() < books.get(j).getId()) {
isItSorted = "true";
boo.add(isItSorted);
} else {
isItSorted = "false";
boo.add(isItSorted);
}
}
}
Adds on the order of 100 million items to the ArrayList boo.
If you want to check if something is sorted you can use much simpler code:
Book prev = books[0];
for (int i = 1; i < books.size(); i++) {
if (prev.getId() > books[i].getId())
return false;
}
return true;
But you shouldn't need to call it inside binarySearch() because that will defeat the purpose of binarySearch() and make it as slow as linearSearch().

Add new objects in arraylist to the other arraylist while the second one do not have these new objects

If I have two ArrayList one for currentScanWifiList<Router>
this contains the current wifi scan Routers data
and the the second have the same type allUniqueRoutersList<Router>
used to add the new routers existing in currentScanWifiList and do not exist in allUniqueRoutersList
the comparing between the routers through equaling its bssid
public class Router {
private String bssid;//identifier
private String ssid;
private double x;
private double y;
private double meanRss;
setter and getter....
}
for (int x = 0; x < currentScanList.size(); x++) {
Router currentTempRouter = currentScanList.get(x);
for (int y = 0; y < allUniqueRoutersList.size(); y++) {
Router allTempRouter = currentScanList.get(y);
if (!currentTempRouter.getBssid().equals(allTempRouter.getBssid())) {
allUniqueRoutersList.add(currentScanList.get(x));
break;
}
}
}
I tried this code but it is not working, it add all objects to allUniqueRoutersList
not the new ones only
what should I do to compare between the two arraylist elements using bssid
This is because you add the router immediately, e.g. you don't really loop:
for (int x = 0; x < currentScanList.size(); x++) {
Router currentTempRouter = currentScanList.get(x);
boolean found = false;
for (int y = 0; y < allUniqueRoutersList.size(); y++) {
Router allTempRouter = allUniqueRoutersList.get(y);
if (currentTempRouter.getBssid().equals(allTempRouter.getBssid())) {
found = true;
break;
}
}
if(!found){
allUniqueRoutersList.add(currentTempRouter);
}
}
Store your bssid's from your allUniqueRoutersList in a set and iterate over your currentScanList and check for each router if the bssid is in the set, if not add the router to your allUniqueRoutersList :
Set<String> bssidSet = allUniqueRoutersList.stream()
.map(router->router.getBssid())
.collect(Collectors.toSet());
for(Router r : currentScanList){
if(!bssidSet.contains(r.getBssid())){
allUniqueRoutersList.add(r);
}
}

Where can I use threads to improve these methods?

Okay so for my project in my CSC330 class I am supposed to use threads to quickly find the answer to queries in a massive data set. Each item in the array is a user and the string is a collection of sites they visited on a website identified by number.
Example (the array is String users[])
users[1] = "1 4 5 7" users[2] = "1 2 7 17 10" users[3] = "6"
The queries are:
are there more than ___ users who looked at X
What percent of users looked at X
Are there more users who looked at X than Y
How many users viewed X ____ number of times
What percent of users looked at X more than Y
there are about a million users in this array and we had to solve the following queries without threads on a smaller scale version of the data for testing. All of mine work. Now I need to switch to the massive text file and I need to use threads to increase the speed. I'm wondering where it would be beneficial and how I could implement these threads. I will supply my code for solving each query. I'm thinking that I could use multiple threads to go through parts of the array at the same time but I'm not sure how to execute this.
public boolean query1(String num, String pageName){
if(num == null){
return false;
}
else
{
int userCount = 0;
int pageNum = convert(pageName);
System.out.println(pageNum);
String pageNumString = Integer.toString(pageNum);
System.out.println(pageNumString);
for(int i = 0; i < users.length; i++ )
{
for(String entry : users[i].split(" "))
{
if(entry.equals(pageNumString))
{
userCount++;
break;
}
}
}
if(userCount > Integer.parseInt(num)){
return false;
}
else{
return true;
}
}
}
public float query2(String pageName){
int userCount = 0;
int pageNum = convert(pageName);
String pageNumString = Integer.toString(pageNum);
for(int i = 0; i < users.length; i++ )
{
for(String entry : users[i].split(" "))
{
if(entry.equals(pageNumString))
{
userCount++;
break;
}
}
}
float percentage = (userCount*100.0f)/users.length;
return percentage;
}
public boolean query3(String pageName, String pageName2){
int userCount1 = 0;
int userCount2 = 0;
String pageNumString = Integer.toString(convert(pageName));
String pageNumString2 = Integer.toString(convert(pageName2));
for(int i = 0; i < users.length; i++ )
{
for(String entry : users[i].split(" "))
{
if(entry.equals(pageNumString))
{
userCount1++;
break;
}
}
for(String entry : users[i].split(" "))
{
if(entry.equals(pageNumString2))
{
userCount2++;
break;
}
}
}
return userCount1 > userCount2;
}
public int query4(String pageName, int numTimes){
int userCount = 0;
String pageNumString = Integer.toString(convert(pageName));
for(int i = 0; i < users.length; i++ )//runs through each user
{ int pageCount = 0;
for(String entry : users[i].split(" "))// runs through each user's pages
{
if(entry.equals(pageNumString))
{
pageCount++; // each time page is found page count increments 1
}
} // once done running through user's pages
if(pageCount == numTimes){ // check if the number of pages is equal to the number given
userCount++; // if so increment userCount
}
}
return userCount;
}
public float query5(String pageName, String pageName2){
int viewedMore = 0;
int userCount1 = 0;
int userCount2 = 0;
String pageNumString = Integer.toString(convert(pageName));
String pageNumString2 = Integer.toString(convert(pageName2));
for(int i = 0; i < users.length; i++ )
{
for(String entry : users[i].split(" ")){
userCount1 = 0;
userCount2 = 0;
if(entry.equals(pageNumString))
{
userCount1++;
break;
}
}
for(String entry : users[i].split(" "))
{
if(entry.equals(pageNumString2))
{
userCount2++;
break;
}
}
if(userCount1 > userCount2){
viewedMore++;
}
}
return viewedMore*100.0f/users.length;
}
At the very least, in query3 and query5 you can spawn off threads for each of the two inner for-loops; there's no reason they must be run sequentially. And for any of the query functions, you can certainly split the array into sections. Since your data is growing large, that approach will most likely be faster than iterating over the data with the main thread.
I would suggest providing the threads contiguous segments (e.g. index 0 to N-1; N to N+N-1 etc). This previous StackOverflow answer provides good reasoning why such an approach is most efficient. Once you get something working, you can play around with the number of threads to optimize.
edit to address your comment below
One approach would be to implement the strategy pattern, such that each of your Query are interchangeable across a client, where an executeQuery() call is the interface method. Think of having a client's call look something like
interface Query {
boolean executeQuery();
}
// client code...
Client client = new Client(...);
client.setQuery(new Query3(String num, String pageNum));
client.query(); // calls query.executeQuery();
Within the concrete Query classes, you can define individual behaviors of what the threads would do. This is a rough example:
public Query3 implements Query {
Query3(String pageNum`, String pageNum2) {
this.pageNum1=pageNum1;
this.pageNum2=pageNum2;
}
boolean executeQuery() {
for(int i = 0; i < users.length; i++ ) {
SearchThread first = new SearchThread(pageNum1);
SearchThread second = new SearchThread(pageNum2);
first.run();
second.run();
}
first.join();
second.join();
return first.userCount > second.userCount;
}
SearchThread extends Thread {
String pageNumString;
int userCount;
SearchThread(String pageNumString) {
this.pageNumString=pageNumString;
}
public void run() {
// do your search over this segment of the array, adding up userCounts
}
Here is another StackOverflow question that describes how to run multiple threads over a single array, with some boilerplate code to use.

ArrayList computation?

How can I add the units he/she enrolled every time the user inputs the code he/she wants to enroll? Is there any way I can add the units every time he/she enroll using arrayList?
int units = 3
arrList.add("A25"+"\t\tCS 212"+"\t\tData Structures\t\t\t\t"+ units);
arrList.add("A26"+"\t\tIT 312"+"\t\tData Base Management System 2\t\t"+ units);
arrList.add("A27"+"\t\tIT 312"+"\t\tData Base Management System 2\t\t"+ units);
System.out.println("\n\t\tCodes to enroll");
for(int i = 0; i < 3; i++,num++)
{
codeNo[i] = scan.next();
}
for (String s : arrList) {
for(int i =0; i < codeNo.length; i++)
if (s.startsWith(codeNo[i])) {
System.out.println("\t\t\t"+s);
/**
* this is what I tried
* units = units + units;
*/
}
}
tuitionFee = ( tuitionFee * units + miscFee ) / 3;
System.out.println("\n\n\t\tTOTAL FEE: ");
System.out.printf("\t\tPrelims: "+"%.2f",tuitionFee);
System.out.printf("\t\tMidTerm: "+"%.2f",tuitionFee);
System.out.printf("\t\tFinals: "+"%.2f",tuitionFee);
HashMap<String,Integer> mapCodeToUnit = new HashMap<String,Integer>();
mapCodeToUnit.put("A25", 3);
mapCodeToUnit.put("A26", 3);
mapCodeToUnit.put("A27", 3);
arrList.add("A25"+"\t\tCS 212"+"\t\tData Structures\t\t\t\t"+ mapCodeToUnit.get("A25"));
arrList.add("A26"+"\t\tIT 312"+"\t\tData Base Management System 2\t\t"+ mapCodeToUnit.get("A26"));
arrList.add("A27"+"\t\tIT 312"+"\t\tData Base Management System 2\t\t"+ mapCodeToUnit.get("A27"));
System.out.println("\n\t\tCodes to enroll");
String codeNo[] = new String[3];
for(int i = 0; i < 3; i++,num++)
{
codeNo[i] = scan.next();
}
int totalNumberOfUnit = 0;
for(int i =0; i < codeNo.length; i++) {
totalNumberOfUnit += mapCodeToUnit.get(codeNo[i]);
}
tuitionFee = ( tuitionFee * totalNumberOfUnit + miscFee ) / 3;
Make a list of EntrollmentDTO instead of list of String
EntrollmentDTO.java
class EntrollmentDTO {
private String id;
private String subId;
private String subName;
// Getters & Setters
}
Make a List of EntrollmentDTO as,
ArrayList<EntrollmentDTO> entrollmentList = new ArrayList<EntrollmentDTO>();
And add details as,
EntrollmentDTO entrollmentDTO = new EntrollmentDTO();
entrollmentDTO.setId("A25");
// set all values
entrollmentList.add(entrollmentDTO);
By this you can easily access needed values from the list.

Constructor of Array Point

I'm working on a programm in which I want my object "this" will be an array of Point but I have this error when I run the programm and I'm not understand why.
Error --> DouglasPeucker.
My programm :
public class DouglasPeucker {
private double epsilon;
protected Point[] coinImage;
public DouglasPeucker(Point [] tab) {
this.coinImage = new Point[tab.length];
for(int i = 0; i < this.coinImage.length; i++) {
double abscisse = tab[i].getX();
double ordonnee = tab[i].getY();
System.out.println(abscisse + " " + ordonnee);
this.coinImage[i].setX(abscisse);
this.coinImage[i].setY(ordonnee);
}
}
You're never assigning a value to coinImage[i], so it will have its default value of null, which you're the dereferencing. You need something like:
for(int i = 0; i < this.coinImage.length; i++) {
double abscisse = tab[i].getX();
double ordonnee = tab[i].getY();
System.out.println(abscisse + " " + ordonnee);
this.coinImage[i] = new Point();
this.coinImage[i].setX(abscisse);
this.coinImage[i].setY(ordonnee);
}
Or preferrably, IMO:
for (int i = 0; i < this.coinImage.length; i++) {
// I'm assuming Point has a sensible constructor here...
coinImage[i] = new Point(tab[i].getX(), tab[i].getY());
// Insert the diagnostics back in if you really need to
}

Categories

Resources