6

The very simple Java code as follows has the weird output, but the same logic code in C and C++ has the right output. I try with the JDK 1.7 and JDK 1.3 (relative JRE), the weird output is always there.

public class Test {

    public static int sum=0;

    public static int fun(int n) {

        if (n == 1)
            return 1;
        else
            sum += fun(n - 1);  // this statement leads to weird output
        // { // the following block has right output
        //     int tmp = fun(n - 1);
        //     sum += tmp;
        // }

        return sum;
    }

    public static void main(String[] arg) {
        System.out.print(fun(5));
    }
}

The output is 1 which should be 8. Relative C/C++ code is as follows:

#include<stdio.h>
int sum=0;
int fun(int n) {

        if (n == 1)
            return 1;
        else
            sum += fun(n - 1);

        return sum;
    }

int main()
{
    printf("%d",fun(5));

    return 0;
}

Adding test java code:

class A {
    public int sum = 0;

    public int fun(int n) {
        if(n == 1) {
            return 1;
        } else {
            sum += fun(n - 1);
            return sum;
        }
    }
}

public class Test {
    public static void main(String arg[]){
        A a = new A();
        System.out.print(a.fun(5));
    }
}
approxiblue
  • 6,982
  • 16
  • 51
  • 59
kingxuke
  • 73
  • 5
  • The details are in the order of evaluation which are different for Java and C++. In Java the result is well defined but counter-intuitive and in C++ it is unspecified. See my answer for the details. – Shafik Yaghmour Feb 25 '14 at 14:49

8 Answers8

6

The problem is this line:

 sum += fun(n - 1);

which is updating the variable sum.

Assuming that you are simply trying to sum the numbers from 1 to N, then it should be doing the calculation that calculates f(N) in terms of f(N - 1). That doesn't require you to refer to sum ... and certainly it doesn't require you to update it.

(I'm being careful NOT to tell you what the answer is ... because it you will learn more if you figure it out yourself.)


By the way, there is nothing Java specific about the flaw in your algorithm ...


It is worth noting that the real issue is not to do with static versus instance variables. The real issue is that a recursive function like this shouldn't be using either kind of variable. Now in this example you can possibly get away with it, but if the recursion involves something like this: f(N) = f(N-1) + f(N-2) you are liable to find that the different call trees interfere with each other.

A more correct solution in this case is to write the method as:

int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n + f(n - 1);
}

As I said, you don't need to refer to, or update the sum variable.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • sum is not an instance variable though, it's static. I agree he's doing the recursion the wrong way, but as far as I can see, the code should still work... Am I missing something? – Yunchi Aug 05 '12 at 03:36
  • @Recursed Ah I finally get it. It's when `sum` is evaluated. `sum += fun(n-1)` is translated into `sum = sum + fun(n-1)`. In Java, the sum inside the expression part of the statement is evaluated **before** fun(n-1) is called, so you get 0. In C++, the sum inside the expression part is evaluated **after** fun(n-1) is called, so you get whatever the result of the last call left sum as. Much more complicated than you'd think... – Yunchi Aug 05 '12 at 03:50
  • @user1576843 Read my comment to Recursed above, I think that's what you got tripped up on... too lazy to type it up as an answer. – Yunchi Aug 05 '12 at 03:57
  • @Woody I think it should has nothing with the static property. See the adding code I have tested. – kingxuke Aug 05 '12 at 04:02
  • @user1576843 Nope, has everything to do with the static property. Seems like I went over your head though... But anyways, you shouldn't use that static variable inside your recursion. All my neighbors here are right, you should think about accepting one of their answers. – Yunchi Aug 05 '12 at 04:10
  • @Woody I get the output 1 whatever the N is. Please check it if you are right. – kingxuke Aug 05 '12 at 04:17
  • @user1576843 There I typed up a long answer. Hope that's clear enough. – Yunchi Aug 05 '12 at 04:30
  • @Stephen So thanks for your answer. In fact, the function is to calculate 2^(n-2) which means each revoke f(N) will double the next return value(f(n-1)),and so I use sum. And I really curious is why the sum just changed once from 0 to 1 when I debug. I thought that is the new jre optimization, so i used jdk1.3(jre1.3), but i have the same output. I have try my best and spent many hours on this question, so if you can give more details, just please. Thanks. – kingxuke Aug 05 '12 at 04:33
  • @Stephen So sorry, I don't know how to accept 2 answers! I missed the opportunity to learn more and now I know your answer really mean. Highly aprreciated! – kingxuke Aug 05 '12 at 05:18
3

I'm going to run through this for fun(3) for the sake of giving a complete answer. For those of you who are not interested why this works for C++ but not for Java, please ignore my answer.

Here is what Java is doing:

inside fun(3)

sum += sum + fn(n-1) // sum is 0

becomes

sum = 0 + fun(2) // sum is 0

Then inside fun(2)

sum = 0 + fun(1) // sum is 0

Then inside fun(1)

return 1 // sum is 0

Back inside fun(2)

sum = 0 + 1; // sum is 0

becomes

sum = 1; // sum will soon become 1

Back inside fun(3)

sum = 0 + 1; // sum is 1

becomes

sum = 1; // sum gets reset to 1

Here is what C++ is doing:

inside fun(3)

sum += fn(n-1) // sum is 0

becomes

sum = sum + fn(2) // sum is 0

Then inside fun(2)

sum = sum + fn(1) // sum is 0

Then inside fun(1)

return 1 // sum is 0

Back inside fun(2)

sum = sum + 1 // sum is 0

Becomes

sum = 0 + 1 => sum = 1 // sum will soon become 1

Back inside fun(3)

sum = sum + 1 // sum is 1

Becomes

sum = 1 + 1 // sum will soon become 2

What you should do: I do not know why C++ evaluates sum after making the function call rather than before. I do not know if this is in the specifications. But I do know that you should not be depending on this in any language. A correct solution would be:

int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n + f(n - 1);
}
Yunchi
  • 5,529
  • 2
  • 17
  • 18
  • It's so clear which make me totally understand! Thank you so much and your advice is also highly appreciated. BTW:Thanks all guys who gave the answer which moved me.Best wishes! – kingxuke Aug 05 '12 at 05:00
  • By the way, 1.9.15 in [the C++ Standard](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) is the relevant part: `Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced` (translation: implementation-defined). It then goes on to give relevant examples. For Java, it's in [15.7.1 in the JLS](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1). – Jason C Feb 25 '14 at 02:51
  • 1
    The C++ portion of this answer is not correct, the order of evaluation is unspecified. – Shafik Yaghmour Feb 25 '14 at 15:00
  • @ShafikYaghmour I'd appreciate it if you read the full answer before posting comments. I did say at the end of my answer that the behavior should not be relied upon and may not be specified. I chose to explain why the poster was seeing the behavior he did, as defined by the implementation, instead of giving the technically correct but rather useless answer of "the order of evaluation is unspecified". – Yunchi Feb 25 '14 at 20:43
  • 1
    I disagree, the answer is meaningless since the behavior is unspecified unless you talk about a specific compiler version, platform etc... you actually have no idea what it is doing. The only way to know would be to disassemble the program and it would *not* be what C++ is doing but what that specific implementation is doing. – Shafik Yaghmour Feb 25 '14 at 20:48
  • @JasonC: _unspecified_ is **not** _implementation-defined_. _implementation-defined_ behavior **must** be documented by the compiler implementation. – ninjalj Jun 15 '14 at 10:20
  • @ShafikYaghmour: even more, it would be what that specific version of that specific implementation at that specific optimization level would be doing. – ninjalj Jun 15 '14 at 10:24
3

The current answers to this question do not get to the root of the issue. The behavior in Java is due to the fact that:

sum += fun(n - 1); 

is equivalent to:

sum = sum + fun(n - 1); 

where sum is evaluated before fun(n - 1) and the value is stored. We can see this by going to the JLS section 15.26.2. Compound Assignment Operators which says:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2))

and then the left hand operand is evaluated and then the right hand operand is evaluated and the operation is performed. So sum is evaluated before each iteration of the recursion and is 0 all the way back.

It also means that if you switched the line to this:

sum = fun(n - 1) + sum ;

it would produce the effect you desire since fun would be evaluated first.

In C++:

sum += fun(n - 1);

is also equivalent to:

sum = sum + fun(n - 1);  

this is covered in the draft C++ standard section 5.17 Assignment and compound assignment operators which says:

The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.[...]

The major different is that the order of evaluation of the left and right hand side is unspecified which is covered in section 1.9 Program execution paragraph 15 which says:

Except where noted, evaluations of operands of individual operators and of subexpressions of individualexpressions are unsequenced.[...]

and so 1 and 8 would both be valid results in C++. We can see this live gcc gives us 8 and clang gives us 1.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
1

Try this:

public class Test {

   public static int fun(int n) {
      System.out.println("processing n " + n );

      if (n == 1)
        return 1;
       else{
           return n + fun(n - 1);
      }
   }

   public static void main(String[] arg) {
      System.out.print(fun(5));
   }
}
Edmon
  • 4,752
  • 4
  • 32
  • 42
  • You just don't understand me. I know the right code. What make me crazy is just why the code I post have the wrong output. – kingxuke Aug 05 '12 at 04:21
1
public static int fun(int n) {
    if (n == 1)
        return 1;
    else
        return n +  fun(n - 1);
} 

BTW if you want to do it in the same way as in C code, just define sum as "Integer" instead of "int"

Grisha Weintraub
  • 7,803
  • 1
  • 25
  • 45
0

You really need to step through your logic there. It doesn't make any sense. Is the "weird" output that f(5) = 8? (you've actually accidentally written a function which computes 2^(n-2), but that seems beside the point)

I can't explain to you where any syntax is wrong - its the algorithm itself. It just doesn't do what you intended it to do. A big red flag that should stand out to you: the variable n isn't even directly added to sum anywhere! When you call fun(5), that 5 isn't even used. It's just passed into f(4).

It seems to me that your logic was this: recursively loop from n->1, and add that to sum. In which case, your function should have been:

void fun(int n){
    if(n == 0)
        return;
    sum += n;
    fun(n-1);
}

or something like that. But that isn't a very...recursive-y way to do things. You'd be much better off with no variables at all. Non-base-case: return n+fun(n-1).

Also in the future, when you say that some code has "weird output", you should probably provide both 1) the weird output and 2) what you expected the output to be. We're all just guessing at what you wanted to write.

Seth Nelson
  • 2,598
  • 4
  • 22
  • 29
  • I Provided that: The output is 1 which should be 8. I think it should output 8 but the java give me 1. So I am confused. BTW,before I post the problem, I spend many hours to find the reason and I try all my best. Until Know I think the Key just isn't here. Whatever, thanks your guys! – kingxuke Aug 05 '12 at 04:31
0
return n <= 1 ? n : n + fun(n-1);
user207421
  • 305,947
  • 44
  • 307
  • 483
0

Try this

   static int getsum(int num){
        int sum = 0;
        if(num == 1){
            return num;

        }else{
             sum  = num+getsum(num-1);  
        }

        return sum ;
    }
Mohammod Hossain
  • 4,134
  • 2
  • 26
  • 37