This question already has an answer here:
Order of operations for compound assignment operators in Java
(1 answer)
Closed 6 years ago.
int a = 2;
int b = 3;
int c = 10;
a +=(a += b) + c; // this will be same as --> a = a+((a= a+b) +c);
/* 2+((2=2+3)+10)
after adding 2+3
now value a = 5;
5 +((5)+10)
5 + (15)
a = 20
*/
System.out.println("a: "+a); // a:17
when i execute above program the answer is a:17 why not a:20 ?
according to me.
After resolving (a += b) now the value of a should be 5 then (a += b) + c would be 15 and after that a +=(a += b) + c; should be 20
please help me understand thanks.
The compound assignment operator stores the original value of the left operand before evaluating the second operand and performing the compound assignment (addition + assignment in this example).
Therefore
a += (a += b) + c;
is equivalent to :
int temp = a;
a = temp + (a += b) + c;
2 + 2 + 3 + 10 = 17
This is a good example of where order of evaluation and precedence don't match. They usually do which makes it confusing when there is a difference, as you mention the expression is evaluated left to right like this
int s = a; // stack
int t = a + b; // second stack value.
s += t;
s += c;
a = t;
a = s;
Note the (a+=b) is the same as (a+b) in this case.
In Java, you can replace a += b with a = a + b. And that is very important.
Hence your expression is equivalent to
a = a + (a = a + b) + c
Note that this will be evaluated as a = Term1 + Term2 + Term3 and in the order left to right.
Term1 is 2.
Term2 is the only tricky one. It is 5 (and has the side-effect of increasing a to 5 but that gets clobbered by the eventual assignment).
Term3 is 10.
That recovers the total, 17.
Note that the behaviour of this expression in C and C++ is undefined.
It is a bit tricky to understand. Although you have a += b on the right-hand side, that doesn't change the value of a that's used for the += at the beginning of the expression; that will still be 2. E.g.:
a += (a += b) + c;
a = 2 + (a[still 2] += 3) + 10
a = 2 + (5) + 10
a = 17
The a += b on the right-hand side is really just a a + b, since this is all on the right-hand side of a += already directed at a.
Details in JLS§15.26.2: Compound Assignment Operators.
First you do a+=b so 2+3 you have 5. Then a+= your answer (here 5) so 2+=5 = 7. Then 7+10 = 17
Related
What is Cardano Triplet ?
If a set of any three positive integers, let's say a, b and c satisfies the condition
cbrt(a + b(sqrt(c)) + cbrt(a - b(sqrt(c)) == 1
Explanation.
if sum of Cubic Root of a + (b * square root of c) and Cubic root of a - (b * square root of c) equals 1 then (a, b, c) is said to be a Cardano triplet.
cbrt represents Cubic Root and sqrt means Square Root.
A integer n will be given, so the numbers a, b and c that we take when added should be lesser than or equal to n.
In short a + b + c <= n.
Constraint : n <= 2^31 -1.
Problem
I've already done something which finds out the correct triplets but when the value of n is greater than 1000 the program runs forever.
public static void cardanoTriplets(long n) {
DecimalFormat decimalFormat = new DecimalFormat("#.###");
long numberOfPairs = 0;
for (long a = 0; a <= n; a++) {
for (long b = 0; b <= n; b++) {
for (long c = 0; c <= (n - a - b); c++) {
if ((a + b + c) == n) {
double val = b * Math.sqrt(c);
double LHS = Double.parseDouble(decimalFormat.format(Math.cbrt(a + val)));
double RHS = Double.parseDouble(decimalFormat.format(Math.cbrt(a - val)));
double addedVal = LHS + RHS;
//System.out.println("RHS and LHS -: ( " + RHS + " , " + LHS + " )");
if (addedVal == 1.0d) {
numberOfPairs++;
//System.out.println(a);
//System.out.println(b);
//System.out.println(c + "\n");
}
}
}
}
}
System.out.println(numberOfPairs);
}
Results
When I pass the value of n as 8, on average the time taken to find the cardano triplet is 31ms and sometimes as low as 16ms. The result was accurate and the result is just one and the triplet is (2, 1, 5).
But when I pass the value of n as 1000, it increases to about 1015ms and the result are not as accurate. It misses out almost 19 triplets. Total number of triplets are 149 for n == 1000.
When the value of n > 1000, let's say 5000, it took 29271ms which is 29 seconds approx and the triplets found are 3364.
Is there any way to reduce time taken to a reasonable amount like less than 5 seconds ?
If so how ?
My Device Specs :
Processor : AMD Ryzen 5 3500U Quad Core
RAM : 8 GB
IDE used : IntelliJ IDEA v2021.2.3 (Community Edition)
Thank you :)
This is a number-theoretical problem; using an imprecise floating point is obviously wrong.
The correct solution requires some math insight. Cardano's name is a great hint.
The expression
cbrt(a + b(sqrt(c)) + cbrt(a - b(sqrt(c))
describes a root of a certain cubic equation. Specifically, the roots of an equation
x^3 + px - q = 0
are
cbrt(q/2 + sqrt((q/2)^2 + (p/3)^3)) + cbrt(q/2) - sqrt(q/2)^2 + (p/3)^3))
Comparing with your problem statement, conclude that a = q/2, and c*b^2 = (q/2)^2 + (p/3)^3
Since a is an integer, q must be even, and since b, c are also integers, p must be divisible by 3. Therefore we are interested in the equations
x^3 + 3ux - 2a = 0
having 1 as a root. That narrows the problem down to searching u, v such that 1 + 3u - 2a = 0. Here u^3 + a^2 = b^2*c. Notice that u must be odd.
All these observations lead to a (pseudo)code:
for u in range(1, n, 2)
a = (1 + 3u)/2
t = u^3 + a^2
find the largest b such that b^2 divides t
c = t / b^2
if a + b + c < n
they are a Cardano triplet
Your first problem, is the loop-in-loop-in-loop what will take 1.000.000.000 rounds for n=1000.
As you know already that n = a + b + c, you can take one loop out. the c-loop
and rewrite as:
for (long a = 0; a <= n; a++) {
for (long b = 0; b <= (n - a); b++) {
long c = n - a - b;
so you go from n * n * n -> n * n
If the equation is n => a + b + c (as in your problem statement), you can use:
for (long a = 0; a <= n; a++) {
for (long b = 0; b <= (n - a); b++) {
for (long c = 0; c <= (n - a - b); c++) {
Second, you are doing a format to a decimal and then convert back to double where as the Math.cbrt gives already a double. I would suggest not doing so.
The problem of "missing 19 triplets" is related to the point above. You only accept 1.0d as the correct answer, there in the previous step you did formatting on the doubles (most likely giving rounding issues). Even if you would take out the formatting, I believe it is better to allow for a bit more rounding error..
something like:
if (0.999 < addedVal && addedVal < 1.001)
However, I have no idea on the math of this equation as there must be a reason why you say there are 149 triplets.. Depending on the rounding for sure you have different answers... I believe there is something like mathemathical proof the triplets are 1.
Last what you can do: I believe the calculation of the Math.cbrt is not that fast. You are repeating this a lot. You can keep track of your calculation by placing the result of the Math,cbrt in a HashSet. The Key is the input and the Value the result of the Math.cbrt.
So first check if you have the Key already in the HashSet, if not calculate the cbrt and place it, if already available us it..
Operator precedence of ++ and -- is higher than + and has associativity right to left. So I think output of x should be (++i+4 then 5+4) 9 and output of y should be (i+++5 then 5+5) 10. For both it print 11 as output.
class First {
public static void main(String[] args) {
int i = 5;
int x = ++i + --i;
int y = i++ + i--;
System.out.println("x="+x);
System.out.println("y="+y);
}
}
Let's translate this:
int i = 5;
int x = ++i + --i;
int y = i++ + i--;
System.out.println("x=" + x);
System.out.println("y=" + y);
Into (step by step):
int i = 5;
int x = ++i + --i;
// ^
// (i = i + 1) + --i
// (i = 5 + 1) + --i
// (i = 6) + --i
// ^
// 6 + (i = i - 1)
// 6 + (i = 6 - 1)
// 6 + (i = 5)
// 6 + 5
// x = 11
int y = i++ + i--;
// ^
// (i = 5) + i--
// 5 + i--
// i = i + 1 // post evaluation, after i was evaluated to 5, now i increments its value and it is 6
// 5 + (i = 6)
// 5 + 6
// y = 11
// i = i - 1 // post evaluation, after i was evaluated to 6, now i decrements its value and it is 5
System.out.println("x=" + x); // x=11
System.out.println("y=" + y); // y=11
Section 15.7 of the JLS is clear on this:
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.
Whenever assignment operators and other binary operators are mixed in with increment or decrement operators, evaluating the expression is not intuitive, and one should never write such confusing code.
Even if the increment and decrement unary operators are right-to-left associative, the values of an expression are evaluated left-to-right.
int x = ++i + --i;
Here, ++i increments i to 6 and returns 6, then --i decrements i to 5 and returns 5, and the sum of 6 and 5 is 11.
int y = i++ + i--;
Here, i++ returns 5 and increments i to 6, then i-- returns 6 and decrements i to 5, and the sum of 5 and 6 is 11.
With these expressions, the right-to-left associativity does not figure into the order of operations, because associativity only controls which operations are executed first among operators of the same precedence. You have a + operator of lower precedence between them, so the associativity doesn't matter here.
Associativity dictates how operators with the same precedence are parsed.
For example, the Unary Minus (- ...) and Unary Bitwise NOT (~ ...) operators have the same precedence and have right-to-left associativity. Therefore:
int i = ~-1; // == 0
int j = -~1; // == 2
This question already has answers here:
Why is "a^=b^=a^=b;" different from "a^=b; b^=a; a^=b;"?
(6 answers)
Closed 6 years ago.
I tried "swap variable in java without temp" in Java and I found something that bothers me:
int a = 1, b = 2;
b^= a ^= b ^= a;
System.out.println(a + " vs " + b);
The output shows
2 vs 0
However if I separate the leftmost assignment as a individual statement:
int a = 1, b = 2;
a ^= b ^= a;
System.out.println(a + " vs " + b);
b^=a;
System.out.println(a + " vs " + b);
The output is
2 vs 3
2 vs 1
Now the output is as expected.
In C++, the evaluation is ensured from right to left. What the difference, in terms of language spec, tells Java could lead such expected result?
According to the JLS, x ^= y is equivalent to x = (x) ^ (y) (there's also a cast in there, but if you're dealing with ints, the cast doesn't matter anyway). So this:
b ^= a ^= b ^= a;
is equivalent to
b = (b) ^ (a = (a) ^ (b = (b) ^ (a)));
// ^^^
In Java, arguments to an operator are always evaluated left to right. So the b that I pointed to in the above is the original value of b, since it's evaluated before the assignment to b in the right part of the expression. That means the expression is not equivalent to
b ^= a;
a ^= b;
b ^= a;
since the third statement uses the new value of b as the left operand to ^.
Here is the reference implementation I got, the confusion is, I think there is no need for recursion. I post both reference code and my thought below here, for the difference see line 5.
Any insights are appreciated. Please feel free to correct me if I am wrong.
Reference implementation:
1 int add_no_arithm(int a, int b) {
2 if (b == 0) return a;
3 int sum = a ^ b; // add without carrying
4 int carry = (a & b) << 1; // carry, but don’t add
5 return add_no_arithm(sum, carry); // recurse
6 }
Another implementation in my thought,
1 int add_no_arithm(int a, int b) {
2 if (b == 0) return a;
3 int sum = a ^ b; // add without carrying
4 int carry = (a & b) << 1; // carry, but don’t add
5 return sum ^ carry;
6 }
BTW, tried 8+8 in Python - worked for me:
Is the recursion needed?
a = 8
b = 8
sum = a ^ b
carry = (a & b) << 1
print(sum^carry) # 16
The second approach doesn't work with 1 + 3.
Here are the steps
a == 01
b == 11
sum = a^b == 10
carry = (a&b) << 1 == 10
sum ^ carry == 00 // wrong answer! 1 + 3 == 4
Just doing ^ at the last step is not enough, as there may be a carry in that sum.
The question is, whether recursive is needed?
Yes, it is. You can see this for yourself by experimenting with other numbers, instead of just 8 + 8. For example, try 21 and 15, without recursion this gives output of 26.
The bitwise XOR operator ^ is only equivalent to the addition operator + if there is no binary carrying in the sums. If there is binary carrying, then they are not equivalent. For example, 8 + 7 equals 8 ^ 7 equals 15, but 8 + 8 is 16 while 8 ^ 8 is 0.
Even if you have calculated the sum-no-carry and the carry-no-sum correctly, what if those two numbers, when added, would produce a binary carry? Then your ^ operator at the end would be incorrect. Without the + operator, the only option I see is to recurse, to add those numbers. This will recur until one of the numbers is 0.
Example:
add(no_arithm(18, 6))
sum = a^b 18 ^ 6 is 20.
carry = (a & b) << 1 18 & 6 is 2, bit shift left 1 is 4.
return sum ^ carry 20 ^ 4 is 16, incorrect (18 + 6 = 24).
Will this work?
import java.util.*;
public class Solution {
public static int sumOfTwoNumbers(int a, int b) {
if (b<0){
for(int j = 1; j<=-b;j++)
--a;
}
if(b>0){
for (int i = 1; i <= b; i++){
a++;
}
}
return a;
}
This question already has answers here:
Difference between a += 10 and a = a + 10 in java? [duplicate]
(5 answers)
Closed 8 years ago.
I've been told that there are differences between a+=b; and a=a+b; which can result in only one of those being legal depending on the type declerations.
Does anyone have an example of this?
There is basically no difference, however there is a subtle difference.
The arithmetic assignment operators do an implicit cast. e.g.
byte a = 1;
int b = 2;
a += b; // compiles
a = a + b; // doesn't compile as a byte + int = int
a = (byte) (a + b); // compiles as this is the same as +=
For more weird examples.
int a = 5;
a += 1.5f;
// a == 6
char ch = '0'; // (char) 49
ch *= 1.1; // ch = '4';
long l = Integer.MAX_VALUE;
l += 0.0f; // i = (long ) ((long ) l + 0.0f)
// i == Integer.MAX_VALUE + 1L; WTF!?
// l is no longer Integer.MAX_VALUE due to rounding error.
The JLS (section 15.26.2) says:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
The presence of the type-cast means that there are a couple of edge cases where a = a op b means something different to a op= b.
See Peter Lawrey's answer for one example. It is when a is a byte and b is an int and the "op" is +. The "gotcha" is a + b produces an int, which then can't be assigned to a ... without a typecast.
The same scenario applies with other types for a and b and for other arithmentic and bitwise operators.
int a = 10;
int b = 20;
a=a+b; // 30
a+=b; // 30
System.out.println(a);
Both will give the same answer.