Random matching without repeat - java

I've worked on the project in my school and stuck with an error. I cannot run this code since it has an error 'randA2 is already defined in method main(String[])' How can I fix it?
String [] A = {"Russia", "Saudi_Arabia", "Egypt", "Uruguay"};
int A1 = A.length;
int randA1 = (int)(Math.random()*A1);
int randA2 = (int)(Math.random()*A1);
int randA3 = (int)(Math.random()*A1);
int randA4 = (int)(Math.random()*A1);
while(randA1 == randA2) {
int randA2 = (int)(Math.random()*A1);
}
while(randA1 == randA3) {
int randA3 = (int)(Math.random()*A1);
}
while(randA2 == randA3) {
int randA3 = (int)(Math.random()*A1);
}
while(randA1 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
while(randA2 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
while(randA3 == randA4) {
int randA4 = (int)(Math.random()*A1);
}
String AnnounceA1 = A[randA1] +" " + "VS" + " " + A[randA2];
System.out.println(AnnounceA1);
String AnnounceA2 = A[randA3] +" " + "VS" + " " + A[randA4];
System.out.println(AnnounceA2);

I would like to use another way to solve your problem, all you need is :
String[] contries = {"Russia", "Saudi_Arabia", "Egypt", "Uruguay"};
Collections.shuffle(Arrays.asList(contries));
String announceA1 = contries[0] + " VS " + contries[1];
System.out.println(announceA1);
String announceA2 = contries[2] + " VS " + contries[3];
System.out.println(announceA2);

If you want to reassign a new value to a variable in Java, then you don't need to declare its type again. So your first while loop should look like this:
while (randA1 == randA2) {
randA2 = (int)(Math.random()*A1);
}
But besides this, your code has a logical problem, and it won't actually generate 4 unique random numbers. Actually, if you control which numbers you accept, they aren't really random. I would go with this version:
String[] teams = { "Russia", "Saudi_Arabia", "Egypt", "Uruguay" };
Set<Integer> rands = new HashSet<>();
while (rands.size() < teams.length) {
rands.add((int)(Math.random()*teams.length));
}
String AnnounceA1 = teams[rands[0]] +" " + "VS" + " " + teams[rands[1]];
System.out.println(AnnounceA1);
String AnnounceA2 = teams[rands[2]] +" " + "VS" + " " + teams[rands[3]];
System.out.println(AnnounceA2);
The strategy in my suggested version of your code is using a set to hold 4 random integers (which is the number of teams in your example). It is a property of sets that every entry has to be unique. So, if we iterate this set, adding random integers, we will eventually end up with 4 unique random integers. Then, we can use them to choose team names to display in your output message.

You redefine your vars inside the while loops.
Ommit the int declaration iside the while loops:
...
while(randA1 == randA2) {
randA2 = (int)(Math.random()*A1);
}
while(randA1 == randA3) {
randA3 = (int)(Math.random()*A1);
}
...
Java is a type safety language. This is different to for example JavaScript. Inside JavaScipt the JIT (Just in time compiler) would not complain, because it is allowed to redefine variables, event if the type is changing.

Related

How to improve a cumbersome comparison of 2 objects' fields

We have a program that compares thousands of pairs of Students by checking each field of the Student and counting the diffs:
class Student{
String name;
String address;
String biologyCourse;
.....
// about 100 other fields
}
And the counter POJO class:
class Counters{
long bothStudentsHaveName;
long onlyLeftHasName;
long onlyRightHasName;
......
// number of fields in Student * 3 (both, only left, only right)
}
Our compare function accepts 2 students plus the counters object and needs to scan the fields and update the relevant counters:
public void compareStudents(Student left, Student right, Counters counters){
if (!StringUtils.isEmpty(left.name) && !StringUtils.isEmpty(right.name) ){
counters.bothStudentsHaveName++;
} else if (StringUtils.isEmpty(left.name) && !StringUtils.isEmpty(right.name)){
counters.onlyRightHasName++;
} else if (!StringUtils.isEmpty(left.name) && StringUtils.isEmpty(right.name))){
counters.onlyLeftHasName++;
}
/// and now??
}
At this point, we can add 100s more triplets of if/else like the above - but we believe there should be a much easier way to do that.
Reflection can be an option or maybe X dimensions arrays, but can we somehow write the code so the comparison and counting will be much more generic?
I have solved your problem with one single loop. But here I'm assuming that naming convention for all the fields will be the same as described in your question. Here I am dynamically accessing the Student fields and updating Counter fields accordingly. Here is the complete solution:
Solution Class:
public class Solution {
public void compareStudents(Student left, Student right, Counter counter) throws Exception {
for (Field field : Student.class.getDeclaredFields()) {
Object leftValue = field.get(left);
Object rightValue = field.get(right);
String fieldName = field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
if(leftValue != null && rightValue != null) {
Field counterField = Counter.class.getDeclaredField("bothStudentsHave" + fieldName);
counterField.set(counter, (long) counterField.get(counter) + 1);
} else if (leftValue != null) {
Field counterField = Counter.class.getDeclaredField("onlyLeftHas" + fieldName);
counterField.set(counter, (long) counterField.get(counter) + 1);
} else if (rightValue != null) {
Field counterField = Counter.class.getDeclaredField("onlyRightHas" + fieldName);
counterField.set(counter, (long) counterField.get(counter) + 1);
}
}
}
}
Student Class:
class Student {
String name;
String address;
String biologyCourse;
}
Counter Class:
class Counter {
// name
long bothStudentsHaveName;
long onlyLeftHasName;
long onlyRightHasName;
// address
long bothStudentsHaveAddress;
long onlyLeftHasAddress;
long onlyRightHasAddress;
// biologyCourse
long bothStudentsHaveBiologyCourse;
long onlyLeftHasBiologyCourse;
long onlyRightHasBiologyCourse;
// ... and so on
#Override
public String toString() {
return "Counter{" + "\n" +
"\tbothStudentsHaveName = " + bothStudentsHaveName + "\n" +
"\t, onlyLeftHasName = " + onlyLeftHasName + "\n" +
"\t, onlyRightHasName = " + onlyRightHasName + "\n" +
"\t, bothStudentsHaveAddress = " + bothStudentsHaveAddress + "\n" +
"\t, onlyLeftHasAddress = " + onlyLeftHasAddress + "\n" +
"\t, onlyRightHasAddress = " + onlyRightHasAddress + "\n" +
"\t, bothStudentsHaveBiologyCourse = " + bothStudentsHaveBiologyCourse + "\n" +
"\t, onlyLeftHasBiologyCourse = " + onlyLeftHasBiologyCourse + "\n" +
"\t, onlyRightHasBiologyCourse = " + onlyRightHasBiologyCourse + "\n" +
'}';
}
}
Tester Class:
public class Tester {
public static void main(String[] args) throws Exception {
// Creating Dummy Variables
Student student1 = new Student();
student1.name = "Test";
student1.biologyCourse = "Yes";
Student student2 = new Student();
student2.name = "Test1";
student2.address = "abc street";
Counter counter = new Counter();
// Comparing Students
Solution solution = new Solution();
solution.compareStudents(student1, student2, counter);
// Printing Counter
System.out.println(counter);
}
}
Output:
Counter{
bothStudentsHaveName = 1
, onlyLeftHasName = 0
, onlyRightHasName = 0
, bothStudentsHaveAddress = 0
, onlyLeftHasAddress = 0
, onlyRightHasAddress = 1
, bothStudentsHaveBiologyCourse = 0
, onlyLeftHasBiologyCourse = 1
, onlyRightHasBiologyCourse = 0
}
If you keep repreating the same basic pattern of fields, then consider extracting that into a class. For example introduce a FieldComparison class that looks a little like this:
public class FieldComparisonCounter {
public int bothHave;
public int onlyLeftHas;
public int onlyRightHas;
// constructor, getters, setters left as an exercise for the reader
}
Then have a Map<String,FieldComparisonCounter> counters somewhere and a method like this:
public void compareField(String fieldName, String leftValue, String rightValue) {
FieldComparisonCounter counter = counters.get(fieldName);
if (counter == null) {
counter = new FieldComparisonCounter();
counters.put(fieldName, counter);
}
boolean leftHas = !StringUtils.isEmpty(leftValue);
boolean rightHas = !StringUtils.isEmpty(rightValue);
if (leftHas && rightHas) {
counter.bothHave++;
} else if (leftHas) {
counter.onlyLeftHas++;
} else if (rightHas) {
counter.onlyRightHas++;
}
}
Then adding a new field comparison is as simple as calling
compareField("name", left.name, right.name);

How to check that the object isn't the last and how to handle it if it is?

I have a method that cycles through a Bag of geometry objects and gets specific attributes and compares the current attribute to the previous, if there is one, with the intention of storing the highest attribute and then assigning it to another geometry.
I've just realised that at some point I am going to reach the end of that Bag and I need my method to alert me that it has reached the last object in the bag.
How can I identify that the current object is the last in the bag?
int getLargestUnassignedWard() {
Bag lsoaGeoms = centroidsLayer.getGeometries();
System.out.println();
System.out.println("Getting Largest Unassigned Wards!");
int highestOSVI = -1;
MasonGeometry myCopy = null;
for (Object o : lsoaGeoms) {
MasonGeometry masonGeometry = (MasonGeometry) o;
int id = masonGeometry.getIntegerAttribute("ID");
String lsoaID = masonGeometry.getStringAttribute("LSOA_NAME");
int tempOSVI = masonGeometry.getIntegerAttribute("L_GL_OSVI_");
Point highestWard = masonGeometry.geometry.getCentroid();
System.out.println(lsoaID + " - OSVI rating: " + tempOSVI + ", ID: " + id);
if (assignedWards.contains(id))
continue;
// tempOSVI = the attribute in the "L_GL_OSVI_" column - ints
if (tempOSVI > highestOSVI) { // if temp is higher than highest
highestOSVI = tempOSVI; // update highest to temp
myCopy = masonGeometry; // update myCopy, which is a POLYGON
}
}
if (myCopy == null) {
System.out.println("ALERT: LSOA Baselayer is null!");
return -1; // no ID to find if myCopy is null, so just return a fake value
}
int id = myCopy.getIntegerAttribute("ID"); // Here, id changes to the highestOSVI
assignedWards.add(id); // add ID to the "assignedWards" ArrayList
System.out.println("Highest OSVI Raiting is: " + myCopy.getIntegerAttribute("L_GL_OSVI_") + " for LSOA ID: "
+ id + " (" + myCopy.getStringAttribute("LSOA_NAME") + ")");
System.out.println("Current list of Largest Unassigned Wards: " + assignedWards); // Prints out: the ID for the highestOSVI
System.out.println();
return myCopy.getIntegerAttribute("ROAD_ID"); // return Road_ID for the chosen LSOA to visit
}
I've just realised that at some point I am going to reach the end of that Bag and I need my method to alert me that it has reached the last object in the bag.
I don't see any reason you need to know that to do what you've said that loop does.
But if you do need to know that within the loop, you can't use the enhanced for loop; you'll need to use the iterator directly:
for (Iterator it = lsoaGeoms.iterator(); it.hasNext(); ) {
MasonGeometry masonGeometry = (MasonGeometry)it.next();
boolean isLast = !it.hasNext();
// ...
}
Side note: I strongly recommend using type parameters rather than raw types and typecasts. For instance, lsoaGeoms would be a Bag<MasonGeometry>, etc.

Out of bounds string array java

I am facing a problem that I can't take elements from ArrayList and push them to my new String array. Actually right now I`m feeling lost. Receiving an exception out of bounds, but after a check with printing elements by their id everything works? By the way my darbuotojuArray looks like this:
Programuotojas: X X 1 X
Here's my code:
public String[] renkantDarbuotojus() {
String[] darbuotojaiIKomanda = new String[2];
if (darbuotojuArray.size() == 0) {
System.out.println("Nera darbuotoju kuriuos butu galima prideti i komanda.");
System.out.println("Pridekite nauju darbuotoju");
meniu.valdiklis();
} else {
for (int i = 0; i < darbuotojuArray.size(); i++) {
System.out.println("ID: " + i + " " + darbuotojuArray.get(i));
}
System.out.println("Pasirinkite pirmaji darbuotoja pagal ID");
Scanner SI = new Scanner(System.in);
int userSelects = Integer.parseInt(SI.nextLine());
darbuotojaiIKomanda[0] = String.valueOf(darbuotojuArray.get(userSelects));
darbuotojuArray.remove(userSelects);
System.out.println("Pasirinkite antraji darbuotoja pagal ID");
int userSelects2 = Integer.parseInt(SI.nextLine());
darbuotojaiIKomanda[1] = String.valueOf(darbuotojuArray.get(userSelects2));
darbuotojuArray.remove(userSelects2);
}
return darbuotojaiIKomanda;
}
So, you are removing an item from the listdarbuotojuArray.remove(userSelects);, which will change all the IDs. You either need to print your list again so the user can select the correct ID, or you can do this:
int userSelects2 = Integer.parseInt(SI.nextLine());
if(userSelects2 == userSelects)
System.out.println("Error, ID has been removed");
else if(userSelects2 > userSelects)
userSelects2 = userSelects2 -1;

ArrayList vs. Array. Why is one working and one isn't?

I've been trying to switch an older assignment over from an array to an arraylist, but for whatever reason my find method is not working when I modify it to use arrayList.. Seems to always be returning -1.
This is part of a large class so I don't want to include everything unless completely necessary, but I did include the declarations in case they are important:
public class Switch {
private SwitchEntry[] camTable;
private int numEntries;
private int maxEntries;
public Switch() {
camTable = new SwitchEntry[100]; // default value
numEntries = 0;
maxEntries = 100;
}
public Switch(int maxEntries) {
camTable = new SwitchEntry[maxEntries];
numEntries = 0;
this.maxEntries = maxEntries;
}
Original:
public int find (MACAddress source) {
int found = -1;
for (int i=0; i < numEntries; i++)
if (source.isEqual (camTable[i].getAddress())){
found = i;
break;
}
return found;
}
Modified:
public int find (MACAddress source) {
int found = -1;
for (int i=0; i < numEntries; i++)
if (source.isEqual (camTable.get(i).getAddress())){
found = i;
break;
}
return found;
}
Where numEntries is modified & where the new entries are added into the arrayList:
public void processFrame(Frame inFrame) {
// first, add source MAC to camTable if not already there
if (find(inFrame.getSource()) == -1) {
if (numEntries >= maxEntries) {
System.out.println ("Error...camTable is full - cannot add " + inFrame.getSource());
} else {
camTable.add(new SwitchEntry(inFrame.getSource(), inFrame.getPort())); //PROBLEM LINE
System.out.println ("Adding " + inFrame.getSource() + " to camTable");
}
}
//process frame
int index = find(inFrame.getDestination());
if (index != -1){
System.out.print ("Sending frame with data " + inFrame.getData() + " from " + inFrame.getSource() + " to " + inFrame.getDestination());
System.out.println (" out port " + camTable.get(index).getPort() );
} else {
System.out.print ("Flooding frame with data " + inFrame.getData() + " from " + inFrame.getSource() + " to " + inFrame.getDestination());
System.out.println (" out all ports" );
}
}
Solution:
camTable.add(numEntries++, new SwitchEntry(inFrame.getSource(),inFrame.getPort()));
Try This
public int find (MACAddress source) {
int found = -1;
ArrayList<MACAddress> camTable = new ArrayList<MACAddress>();
ListIterator<MACAddress> itr = camTable.listIterator();
while(itr.hasNext()){
MACAddress tempAdd = itr.next();
if(source.getAddress().equals(tempAdd.getAddress())){
found = itr.nextIndex();
return found;
}
return found;
}
I assume in ArrayList you store the objects of MACAddress. in if condition i check the source.getAddress to tempAdd.getAddress() is same then it will retun index of ArrayList. here ArrayList is local variable but you can create as a class variable
Use Contain method of collection.(ArrayList)
http://www.tutorialspoint.com/java/util/arraylist_contains.htm
Solution was straight-forward and just an oversight by me. All I had to do was add numEntries back into my add statement, which I neglected to fix after changing from an array to arrayList
Solution is posted in the original question now:

Set int value in conditional statement after declaring string

I have a string (breakmsg) that I would like to be the same for every if statement, with an integer value that needs to change depending on what it is declared as inside the if statement. How would I go about changing the value of the value variable after I have already declared the breakmsg string? Previous attempts are commented inside the code.
Here is my current code:
private int value;
public void setValue(int v){
value = v;
}
#EventHandler
public void onBlockBreak(BlockBreakEvent e) {
Block b = e.getBlock();
Player p = e.getPlayer();
//int value = 0;
String breakmsg = ChatColor.GREEN + "You gained " + ChatColor.GOLD + value + ChatColor.GREEN + " points for collecting " + ChatColor.AQUA + b.getType() + ChatColor.GREEN + ".";
#SuppressWarnings("deprecation")
int itemID = p.getItemInHand().getTypeId();
if (b.getType() == Material.DIAMOND_ORE) {
if (itemID == 257 || itemID == 278) {
//value = 5;
setValue(5);
int points = getConfig().getInt("players." + p.getUniqueId() + ".points");
getConfig().set("players." + p.getUniqueId() + ".points", points + value);
saveConfig();
startScoreboard();
e.getPlayer().sendMessage(breakmsg);
}
}
if (b.getType() == Material.GOLD_ORE) {
if (itemID == 257 || itemID == 285 || itemID == 278) {
//value = 3;
setValue(3);
int points = getConfig().getInt("players." + p.getUniqueId() + ".points");
getConfig().set("players." + p.getUniqueId() + ".points", points + value);
saveConfig();
startScoreboard();
e.getPlayer().sendMessage(breakmsg);
}
}
}
You could wrap that logic in a private function.
private String generateBreakMsgFrom(Block block, int value) {
return ...
}
Then within the if statements rather than setting value you can do:
breakmsg = generateBreakMsgFrom(b, 5);
Please note that you could and should probably also simply set the message after the if statements, but still you want to extract the message generation logic into a private function.
I would really recommend you to read Clean Code. Your function is very long, it is doing multiple things, it has a lot of magic numbers and quite a lot of code duplication...
EDIT: Since you declared int value in your function I haven't realized that it was an instance variable, therefore.
private String generateBreakMsgForBlock(Block block) {
return ... //you can use value from here once properly set
}

Categories

Resources