0

My try

double sum_squares_from(double x, double n){

    return n<=0 ? 0 : x*x + sum_squares_from((x+n-1)*(x+n-1),n-1);

}

Instead of using loops my professor wants us to write functions like this... What the exercise asks for is a function sum_squares_from() with double x being the starting number and n is the number of number. For example if you do x = 2 and n = 4 you get 2*2+3*3+4*4+5*5. It returns zero if n == 0.

My thinking was that in my example what I have is basically x*x+(x+1)(x+1)+(x+1+1)(x+1+1)+(x+1+1+1)(x+1+1+1) = (x+0)(x+0)+(x+1)(x+1)+(x+2)(x+2)+(x+3)(x+3) = (x+n-1)^2 repeated n times where n gets decremented every time by one until it becomes zero and then you sum everything.

Did I do it right?

(if my professor seems a bit demanding... he somehow does this sort of thing all in his head without auxiliary calculations. Scary guy)

  • Don't use `double n`, use `int n` otherwise risk problems with `n <= 0`. [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) and [Why Are Floating Point Numbers Inaccurate?](https://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate) – David C. Rankin Oct 09 '19 at 00:41
  • Does your professor insist the code must be recursive? – pjaj Oct 09 '19 at 00:43
  • @pjaj Yes. He says it's "good coding practice" – SilenceOnTheWire Oct 09 '19 at 00:43
  • I think recursive call should be `sum_squares_from((x+n-1),n-1)` or even just `sum_squares_from(x+1,n-1)` – pjaj Oct 09 '19 at 00:44
  • Why can't you just call it with different values and test it out? – Omid CompSCI Oct 09 '19 at 00:46
  • @OmidCompSCI Because if it was wrong ti would be hard to tell where it went wrong. – SilenceOnTheWire Oct 09 '19 at 00:49
  • As for recursion being "good coding practice" I would debate that with your prof! In 40 years of programming I never used it once. But that discussion is off topic. – pjaj Oct 09 '19 at 00:50
  • I Agree with pjaj, recursion is rarely used and hard to debug/understand for the very reason you mentioned. – Omid CompSCI Oct 09 '19 at 00:51
  • 2
    @pjaj Recursion is common when dealing with recursive problems... not every domain has those, but it's a good tool to have. – obe Oct 09 '19 at 00:53
  • @OmidCompSCI The other problem is that each recursive pass has to be pushed onto the stack (or similar mechanism) then the whole lot unravelled back to the top when the exit condition is reached. this can be slow and use a lot of memory. OK, maybe, if you have a large, fast CPU, but in a microcontroller (Arduino or similar) problematic. – pjaj Oct 09 '19 at 00:59
  • 1
    Another problem with double n is that if n was so large that n-1 == n then you get infinite regress. – dmuir Oct 09 '19 at 08:58

4 Answers4

3

It's not recursive, but it's one line:

int 
sum_squares(int x, int n) {
  return ((x + n - 1) * (x + n) * (2 * (x + n - 1) + 1) / 6) - ((x - 1) * x * (2 * (x - 1) + 1) / 6);
}

Sum of squares (of integers) has a closed-form solution for 1 .. n. This code calculates the sum of squares from 1 .. (x+n) and then subtracts the sum of squares from 1 .. (x-1).

chash
  • 3,975
  • 13
  • 29
  • The reason I did it my way was to allow for non-integer `x`. I think yours also works for non-integer `x`, but you will have to explain the math. UV for you none-the-less. – jxh Oct 09 '19 at 01:38
2

The original version of this answer used ASCII art.

So,

  • &Sum;i:0..n i = n(n+1)(&half;)
  • &Sum;i:0..n i2 = n(n+1)(2n+1)(&frac16;)

We note that,

  • &Sum;i:0..n (x+i)2
    &equals; &Sum;i:0...n x2 + 2xi + i2
    &equals; (n+1)x2 + (2x)&Sum;i:0..n i + &Sum;i:0..n i2
    &equals; (n+1)x2 + n(n+1)x + n(n+1)(2n+1)(&frac16;)

Thus, your sum has the closed form:

double sum_squares_from(double x, int n) {
    return ((n-- > 0)
            ? (n + 1) * x * x
              + x * n * (n + 1)
              + n * (n + 1) * (2 * n + 1) / 6.
            : 0);
}

If I apply some obfuscation, the one-line version becomes:

double sum_squares_from(double x, int n) {
    return (n-->0)?(n+1)*(x*x+x*n+n*(2*n+1)/6.):0;
}

If the task is to implement the summation in a loop, use tail recursion. Tail recursion can be mechanically replaced with a loop, and many compilers implement this optimization.

static double sum_squares_from_loop(double x, int n, double s) {
    return (n <= 0) ? s : sum_squares_from_loop(x+1, n-1, s+x*x);
}

double sum_squares_from(double x, int n) {
    return sum_squares_from_loop(x, n, 0);
}

As an illustration, if you observe the generated assembly in GCC at a sufficient optimization level (-Os, -O2, or -O3), you will notice that the recursive call is eliminated (and sum_squares_from_loop is inlined to boot).

Try it online!

jxh
  • 69,070
  • 8
  • 110
  • 193
  • The ASCII-art alone is worth the UV. Like the closed form solution. – David C. Rankin Oct 09 '19 at 01:11
  • Nice ASCII art, but does this work? I'm getting `86` for `sum_squares_from(2, 4)`. – chash Oct 09 '19 at 01:16
  • @DavidC.Rankin: Thanks, UV for yours as well. – jxh Oct 09 '19 at 01:36
  • Instead of `... + n * (n + 1) * (2 * n + 1) / 6 : 0);`, perhaps `... + n * (n + 1) * (2 * n + 1) / 6 + sum_squares_from(0.0, 0) : 0);`? Now the function meets the [must be recursive](https://stackoverflow.com/questions/58295600/writing-a-function-that-calculates-the-sum-of-squares-within-a-range-in-one-line#comment102954834_58295600) [dunsel](https://en.wiktionary.org/wiki/dunsel) requirement. – chux - Reinstate Monica Oct 09 '19 at 02:11
  • @chux: I didn't read any requirement for recursive, although there was an ask for a single line. I relaxed that requirement into a single expression... – jxh Oct 09 '19 at 16:40
  • Hidden recursion requirement [here](https://stackoverflow.com/questions/58295600/writing-a-function-that-calculates-the-sum-of-squares-within-a-range-in-one-line/58295791?noredirect=1#comment102954834_58295600) and [here](https://stackoverflow.com/questions/58295600/writing-a-function-that-calculates-the-sum-of-squares-within-a-range-in-one-line/58295791?noredirect=1#comment102954841_58295600). – chux - Reinstate Monica Oct 09 '19 at 22:40
  • @chux: I will assume recursion is a requirement only if there is a need for iteration. – jxh Oct 09 '19 at 23:44
1

As mentioned in my original comment, n should not be type double, but instead be type int to avoid floating point comparison problems with n <= 0. Making the change and simplifying the multiplication and recursive call, you do:

double sum_squares_from(double x, int n)
{
    return n <= 0 ? 0 : x * x + sum_squares_from (x + 1, n - 1);
}

If you think about starting with x * x and increasing x by 1, n times, then the simple x * x + sum_squares_from (x + 1, n - 1) is quite easy to understand.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Yes that's exactly the code I commented 30 minutes ago. I agree with you about the `int n` – pjaj Oct 09 '19 at 01:30
0

Maybe this?

double sum_squares_from(double x, double n) {
    return n <= 0 ? 0 : (x + n - 1) * (x + n - 1) + sum_squares_from(x, n - 1);
}
obe
  • 7,378
  • 5
  • 31
  • 40
  • Aren't they equivalent? – SilenceOnTheWire Oct 09 '19 at 00:49
  • Aren't what equivalent..? – obe Oct 09 '19 at 00:50
  • 1
    Isn't `return n<=0 ? 0 : x*x + sum_squares_from(x+1,n-1);` even simpler? – pjaj Oct 09 '19 at 00:54
  • 2
    @pjaj your first recursion in 40 years and you're acing it :) – obe Oct 09 '19 at 00:56
  • @SilenceOnTheWire they are not equivalent.. you are passing the result of a multiplication as "x" in the recursive call... – obe Oct 09 '19 at 00:57
  • @obe Right I just realized I mixed up this problem with another one and forgot to edit later... thanks – SilenceOnTheWire Oct 09 '19 at 00:59
  • @pjaj Would it be wrong if I used x+n-1 instead of x+1? It would make the sums be made the other way around (from the smallest to the greatest value) but it would still be correct I think? – SilenceOnTheWire Oct 09 '19 at 01:16
  • @SilenceOnTheWire Yes it would still give the same answer. As you say it would just sum the squares in a different order. However, as Henry ford is supposed to have said "Add lightness and simplicate" In other words the simplest code that does the job is also the easiest to read and understand and requires less calculation. Doing it your way requires an addition and a subtraction at each step. my way only needs an addition. – pjaj Oct 09 '19 at 01:26