"Comparison method violates its general contract" - I cannot detect any intransitivity [duplicate] - java

This question already has answers here:
Java error: Comparison method violates its general contract
(13 answers)
Closed 7 years ago.
Edit: why I think this is not a duplicate: As biziclop wrote, the problem here is not intransitivity (a>b & b>c => a>c) as in the other problems mentioned here, but that the clause a>b => -(b>a) is violated, so it's a different problem.
I am receiving the following error message:
Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(Unknown Source)
at java.util.TimSort.mergeAt(Unknown Source)
at java.util.TimSort.mergeForceCollapse(Unknown Source)
at java.util.TimSort.sort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at construct.Repair.regretRepair(Repair.java:101)
at lns.One.repaired(One.java:122)
at lns.One.segment(One.java:68)
at lns.One.lnsSolution(One.java:35)
at lns.All.lnsSolutions(All.java:22)
at barter.Genetic.initialPopulation(Genetic.java:36)
at barter.Genetic.run(Genetic.java:26)
at program.Main.main(Main.java:22)
This is where it happens:
Arrays.sort(regretProfits, new Comparator<RegretProfit>(){
#Override
public int compare(RegretProfit first, RegretProfit second){
if (first.possibleRoutes <= 0){
if (second.possibleRoutes > 0){
return 1;
}
return 0;
}
if (first.possibleRoutes < solution.schedule.size() - kay + 1){
if (first.possibleRoutes < second.possibleRoutes){
return -1;
}
if (first.possibleRoutes > second.possibleRoutes){
return 1;
}
if (first.profit > second.profit){
return -1;
}
if (first.profit < second.profit){
return 1;
}
}
if (first.regret > second.regret){
return -1;
}
if (first.regret < second.regret){
return 1;
}
return 0;
}
;});
And this is the class where the object RegretProfit is defined:
public class RegretProfit {
public int[] order;
public double regret;
public double profit;
public int possibleRoutes;
}
The error occurs only every few thousand iterations. I would be really thankful if anybody had some ideas what the problem might be. I've read that intransitivity can cause this exception but I really couldn't figure out where I possibly went wrong.
Solved it, thanks to biziclop!
Arrays.sort(regretProfits, new Comparator<RegretProfit>(){
#Override
public int compare(RegretProfit first, RegretProfit second){
if (first.possibleRoutes <= 0 && second.possibleRoutes > 0){
return 1;
}
if (first.possibleRoutes <= 0 && second.possibleRoutes <= 0){
return 0;
}
if (first.possibleRoutes > 0 && second.possibleRoutes <= 0){
return -1;
}
if (first.possibleRoutes < solution.schedule.size() - kay + 1 || second.possibleRoutes < solution.schedule.size() - kay + 1){
if (first.possibleRoutes < second.possibleRoutes){
return -1;
}
if (first.possibleRoutes > second.possibleRoutes){
return 1;
}
if (first.profit > second.profit){
return -1;
}
if (first.profit < second.profit){
return 1;
}
}
if (first.regret > second.regret){
return -1;
}
if (first.regret < second.regret){
return 1;
}
return 0;
}
;});

It isn't transitivity that is the key problem here, the part of the contract violated is sgn(compare(x, y)) == -sgn(compare(y, x))
If you have these records for example:
first.possibleRoutes = -1; first.regret = 1
second.possibleRoutes = 1; second.regret = -1
Your comparator returns 1. But if you swap them:
first.possibleRoutes = 1; first.regret = -1
second.possibleRoutes = -1; second.regret = 1
Your comparator still possibly returns 1.
Looking at the code there are two suspicious, non-symmetric constructs:
if (first.possibleRoutes <= 0){
if (second.possibleRoutes > 0){
return 1;
}
return 0;
}
Here there's no matching -1 return if first and second are reversed. You also treat every item with possibleRoutes <= 0 as equal, which is probably not what you want.
if (first.possibleRoutes < solution.schedule.size() - kay + 1){
Here you enter a branch based purely on the value of first, which means this branch can also potentially lead to sgn(compare(x, y)) != -sgn(compare(y, x)).
Of course it is possible that under the additional constraints of the full system the two problems cancel each other out (clearly they don't in this case), but it's a very brittle way of designing comparators and I'd advise you to make sure that all branches are symmetrical. It makes it a lot easier to reason about the correctness of your code.

Related

(Java) if statement not evaluating equality between int values

int count = charFreq.get(guessChar);
int matchedChars = updatedCharFreq.get('_');
if (updatedKeyVals.contains('_')) {
if (count == matchedChars) {
;
}
if (count < matchedChars) {
;
}
else {
count = count - matchedChars;
}
Method works if count < matchedChars, and also the else statement. It just skips past the if equality statement. I have been trying to figure it out, but just can't seem to.
As commented, you neglected to chain the first if with an else.
I suggest accounting for all cases explicitly plus an extra final case that should never be reached. The extra check is for defensive programming, to guard against editing errors.
if (count == matchedChars) {
// No code needed here.
} else if (count < matchedChars) {
// No code needed here.
} else if (count > matchedChars) {
count = count - matchedChars;
} else {
throw new IllegalStateException( … ) ; // Should never reach this point.
}
It seems you only care about the case where the first number is bigger than the second. So we could shorten this code.
if (count > matchedChars) {
count = count - matchedChars;
}
Alternatively, you can use the static method Integer.compare. To quote the Javadoc:
Returns: the value 0 if x == y; a value less than 0 if x < y; and a value greater than 0 if x > y
if( Integer.compare( count , matchedChars ) > 0 ) {
count = count - matchedChars;
}

How to fix Out Of Bounds Exception error? [duplicate]

This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 3 years ago.
I wrote in an exam a backtracking code that moving between cell and counting paths, the conditions are if the next cell is i+k or j+k or i-k or j-k.
the code somehow is still going out of bounds even due I tried to prevent it from doing so.
I tried adding fixing the code by adding +1 in the checking valid move in the "if" conditions.
public class Test3
{
public static int howManyPaths(int [][] mat)
{
return howManyPaths(mat,0,0,0);
}
private static int howManyPaths(int [][] mat, int i, int j, int count)
{
if(i == mat.length-1){return count;}
if(j == mat[i].length-1){return howManyPaths(mat,i+1,0,count);}
count = pathCount(mat,i,j);
return howManyPaths(mat,i,j+1,count);
}
private static int pathCount(int [][] mat, int i, int j)
{
int k = mat[i][j];
if(i < mat.length-1 && mat[i][j] == mat[i+k][j] && i+k < mat.length-1){
return 1 + pathCount(mat,i+k,j);}
if(j < mat[i].length-1 && mat[i][j]==mat[i][j+k] && j+k < mat[i].length-1){
return 1 + pathCount(mat,i,j+k);}
if(i > 0 && mat[i][j]==mat[i-k][j] && i-k > 0){
return 1 + pathCount(mat,i-k,j);}
if(j > 0 && mat[i][j]==mat[i][j-k] && j-k > 0){
return 1 + pathCount(mat,i,j-k);}
return 1;
}
}
the expected is how many paths and the actual results are "out of bounds".
Edited with the fixet code still not working
In your code,
if(i < mat.length-1 && mat[i][j] == mat[i+k][j]){
return 1 + pathCount(mat,i+k,j);}
what will it be, if i+k>=mat.length? Or,
if(j < mat[i].length-1 && mat[i][j]==mat[i][j+k]+1){
return 1 + pathCount(mat,i,j+k);
what will it be, if j+k>=mat[i].length? Or,
if(i > 0 && mat[i][j]==mat[i-k][j]){
return 1 + pathCount(mat,i-k,j);}
what will it be, if i-k<0? Or,
if(j > 0 && mat[i][j]==mat[i][j-k]){
return 1 + pathCount(mat,i,j-k);}
what will it be, if j-k<0?
Handle these cases as well. See, being i>0, still i-k can be less than or equal to 0. This is the trick in your case.
Hope you got it now.
Edit:
After you editing your code still it is getting the same exception as you are accessing the indexes and then checking if the index is a valid one.
Not this:
if(i < mat.length-1 && mat[i][j] == mat[i+k][j] && i+k < mat.length-1)
Instead, you must write:
if(i < mat.length-1 && i+k < mat.length-1 && mat[i][j] == mat[i+k][j])
Same for others as well.
By doing this, you will be able to get rid of java.lang.ArrayIndexOutOfBoundsException

Java > Array-2 > twoTwo

problem
Given an array of ints, return true if every 2 that appears in the array is next to another 2.
twoTwo({4, 2, 2, 3}) → true
twoTwo({2, 2, 4}) → true
twoTwo({2, 2, 4, 2}) → false
my code is only mising this case
twoTwo({2, 2, 7, 2, 1}) → false; but returns true;
my code
public boolean twoTwo(int[] nums) {
int notFound = 0;
int i = 0;
boolean found = false;
if (nums.length == 0) {
return true;
}
if (nums.length == 1 && (nums[0] != 2)) {
return true;
}
for (i = 0; i < nums.length - 1; i++) {
if ((nums[i] == 2 && nums[i + 1] == 2)) {
found = true;
}
if (nums[nums.length - 1] == 2 && nums[nums.length - 2] != 2) {
return false;
}
if (nums[i] != 2) {
notFound++;
}
}
if (nums[i] != 2) {
notFound++;
}
if (notFound == nums.length) {
return true;
}
return found;
}
There is never a "wrong" way to code a working solution, but there are bad ways. In your solution, I think you try to handle every individual case in chaotic ways instead of tackling the overarching problem. You have floating variables all over the place and hard coded numbers that are very specific to each case. You have unnecessary and excessive returns.
My suggestion is to work on solving your own question "Return true if all 2's are next to another 2" - instead of trying to code for each specific case. You aren't REALLY solving a problem if you are hard coding to work on a specific subset of that problem.
Just my critique; keep working at it.
Consider refactoring your for loop with this as a starting point, see if you can figure out the logic (semi pseudo code):
for(int i = 1; i < nums.length-1; i++) { // Why do I start i at 1?
if(nums[i]==2) {
if(nums[i-1] == 2 || nums[i+1] == 2) // What does this if check?
do something; // What to do here? Look up the 'continue' keyword.
else
return false;
}
}
return true;
You will find this for loop is JUST a starting point. There will be more needed to add, but hopefully a good jumping point for you.
Best of luck!
public boolean twoTwo(int[] nums)
{
if (nums.length == 1 && nums[0] == 2)
return false;
for (int i = 0; i < nums.length - 1; i++)
if (nums[i] == 2)
if (nums[i + 1] != 2 && (i > 0 && nums[i - 1] != 2))
return false;
return true;
}
Basically this goes through each number in the list, and if it finds a 2, it checks it against the previous and next numbers. That's all it does.

Why does this code have a missing return statement error?

If I change the else if portion of this code to an else statement it runs without any problems so I get how to make it run. What I'm a little confused about is why I get a missing return statement error when it is in its current form. My return is dependent on the value of the boolean variable of negative. I covered the true and false states, isn't that good enough to cover everything?
Or is it that I always have to have a return statement within an else or to add a meaningless return true to the bottom of my function for the compiler to accept my code as covering every case?
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
public boolean posNeg(int a, int b, boolean negative) {
if (!negative) {
return (a < 0 && b > 0) || (a > 0 && b < 0);
}
else if (negative) {
return (a < 0 && b < 0);
}
}
public static void main (String[] args) throws java.lang.Exception
{
}
}
When the compiler sees else if without an else or a trailing return statement, it cannot be certain that all control paths will lead to a valid return statement.
The compiler can be smart at times, but it can't be smart in this situation (nor should it be).
This behavior is helpful in your example: there's absolutely no reason for you to use an else if in this situation. A simple else is easier to read, more concise, and less error prone.
An else is very expressive in this case. It means "the opposite of the if clause" which will still be the case if the code changes in the future.
Your current code would be more likely to contain and/or introduce a bug if the compiler allowed it.
Here's how I would rewrite the method body:
if (negative) {
return (a < 0 && b < 0);
}
else {
return (a < 0 && b > 0) || (a > 0 && b < 0);
}
In general you should prefer if (negative) over if (!negative) unless there's a compelling reason (ie readability) to do otherwise.
Also, a lot of people (including myself) try to put the most simple clause first in an if/else statement. Making your code easy to read is a good thing.
Check out StephenC's answer for a technical explanation and more background about why the compiler behaves this way.
Other questions have explained what the error message means from an intuitive perspective. However the "The compiler is smart, but not perfect!" comment is missing the point.
In fact, the Java compiler is calling your example an error because the Java Language Specification requires it to call it an error. The Java compiler is not permitted to be "smart" about this.
Here is what the JLS (for Java 71) actually says, and how it applies to a simplified version of the incorrect example, and then a corrected version.
"If a method is declared to have a return type, then a compile-time error occurs if the body of the method can complete normally (JLS 14.1). In other words, a method with a return type must return only by using a return statement that provides a value return; it is not allowed to "drop off the end of its body". " - JLS 8.4.7
(Read JLS 14.1 for the definition of "normal completion" ...)
And the rules for deciding whether a "normal" completion is possible are the reachability rules in JLS 14.21. And they say:
"An if-then statement can complete normally iff it is reachable."
"An if-then-else statement can complete normally iff the then-statement can complete normally or the else-statement can complete normally."
"A break, continue, return, or throw statement cannot complete normally."
(Where 'iff' means "if and only if" ...)
Consider a simplified version of the example:
public int test(boolean a) {
if (a) {
return 1;
}
else if (!a) {
return 0;
}
}
In this example, the else-statement is an if-then which can complete normally by rule #1. Therefore, by rule #2, the if-then-else statement can also complete normally. But that is a compilation error, because JLS 8.4.7 says that a method with a return type cannot complete normally.
But if you change the example to this ...
public int test(boolean a) {
if (a) {
return 1;
}
else {
return 0;
}
}
Now by rule #3, both the if-statement and the else-statement cannot complete normally. So by rule #2, the entire if-then-else cannot complete normally. That is what it required by JLS 8.4.7 ... therefore no compilation error.
1 - The Java 8 JLS will say essentially the same thing, though the section numbers may be different ...
I think you already know that the second if is redundant.
if (negative) is interpreted context-free, which means that the compiler ignores that if(!negative) has already been handled.
if => else if => where is the else condition?
For a return statement, all branches (conditions) must be handled.
You could do:
if (!negative)
return (a < 0 && b > 0) || (a > 0 && b < 0);
return (a < 0 && b < 0);
or:
if (!negative)
return (a < 0 && b > 0) || (a > 0 && b < 0)
else
return (a < 0 && b < 0);
or (my preferred way):
return negative ? (a < 0 && b < 0) : (a < 0 && b > 0 || a > 0 && b < 0)
However, I'd recommend to avoid the negative condition, it's harder for the human brain in complex scenario. Even some Java IDEs like IntelliJ helps to find those patterns to fix them.
You'd end up with:
if (negative)
return (a < 0 && b < 0);
else
return (a < 0 && b > 0) || (a > 0 && b < 0);
You do not need "if (negative) {}" in the "else" brace
You should have a return in all your branches.
public boolean posNeg(int a, int b, boolean negative) {
if (!negative) {
return (a < 0 && b > 0) || (a > 0 && b < 0);
}
else if (negative) {
return (a < 0 && b < 0);
}
}
The method above logically has a return in all branches, but technically it does not. We like the Java compiler to be fast, therefore it is undesirable to have a Java compiler analyzing semantically the code.
public boolean posNeg(int a, int b, boolean negative) {
if (!negative) {
return (a < 0 && b > 0) || (a > 0 && b < 0);
}
else {
return (a < 0 && b < 0);
}
}
The method above has a return in all the branches.
public boolean posNeg(int a, int b, boolean negative) {
if (!negative) {
return (a < 0 && b > 0) || (a > 0 && b < 0);
}
return (a < 0 && b < 0);
}
However, as you can see above, you do not even need the else, because if the code ever reaches the second return, then negative is certainly false, as if it was true, the first return would end the algorithm.
public boolean posNeg(int a, int b, boolean negative) {
return ((negative) && (a < 0 && b < 0)) || ((!negative) && (a < 0 && b > 0) || (a > 0 && b < 0));
}
The method above is a one-liner.
public boolean posNeg(int a, int b, boolean negative) {
return ((negative) && (a < 0 && b < 0)) || ((!negative) && ((a < 0) == (b > 0)));
}
The method above uses the fact that in the second case the positivity of a is equal with the negativity of b.

java.lang.ArrayIndexOutOfBoundsException: -1 error in my java program

I seem to get an error when I test running my program, which says java.lang.ArrayIndexOutOfBoundsException: -1
Please can anyone give me some advice on how to fix this?
class MineFinderModel {
public static int MINE_SQUARE = 10;
public static int EMPTY_SQUARE = 0;
int num_of_cols;
int num_of_rows;
int[][] the_minefield;
public MineFinderModel(int n_cols, int n_rows) {
num_of_rows = n_rows;
num_of_cols = n_cols;
the_minefield = new int[num_of_cols][num_of_rows];
}
public boolean addMine(int thisCol, int thisRow) {
if (thisCol >= num_of_cols || thisRow >= num_of_rows)
return false;
if (the_minefield[thisCol][thisRow] == MINE_SQUARE)
return false;
the_minefield[thisCol][thisRow] = MINE_SQUARE;
return true;
}
public int getValue(int thisCol, int thisRow) {
if (thisCol >= num_of_cols || thisRow >= num_of_rows)
return 0;
return the_minefield[thisCol][thisRow];
}
public void addMinesToCorners() {
the_minefield[0][0] = MINE_SQUARE;
the_minefield[0][num_of_rows -1] = MINE_SQUARE;
the_minefield[num_of_cols - 1][0] = MINE_SQUARE;
the_minefield[num_of_cols - 1][num_of_rows - 1] = MINE_SQUARE;
}
}
I guess that it should be in the "addMinesToCorners()" function since you are not testing the boundaries.
What about trying to put some if around you variables ?
if(num_of_cols == 0)
if(num_of_rows == 0)
At initialization, this equals "0", and then "0 - 1" gives "-1". Hence the error.
Hope this helps !
All your methods have the max value check but none of them are checking for a negative value in thisRow and thisCol so the addMine() and getValue() will throw an java.lang.ArrayIndexOutOfBoundsException if any of the arguments to these 2 methods is negative.
You can add a condition like
`
if (thisCol >= num_of_cols || thisCol < 0
|| thisRow >= num_of_rows || thisRow <0)
return false
Arrays are zero indexed so your checks are incorrect, e.g., if num_of_cols is 10 then the last position will be 9 but your check will pass if you pass in 10 as thisCol because it is checking against the initialiser value instead of the length of the array.
Try changing your test to
if (thisCol < 0 thisCol >= (num_of_cols - 1) || thisRow < 0 || thisRow >= num_of_rows - 1))

Categories