问题
I was asked to calculate the following nested root expression using recursion only.
I wrote the code below that works, but they allowed us to use only one function and 1 input n
for the purpose and not 2 like I used.
Can someone help me transform this code into one function that will calculate the expression? cant use any library except functions from <math.h>
.
output for n=10: 1.757932
double rec_sqrt_series(int n, int m) {
if (n <= 0)
return 0;
if (m > n)
return 0;
return sqrt(m + rec_sqrt_series(n, m + 1));
}
double helper(int n) {
return rec_sqrt_series(n, 1);
}
回答1:
Use the upper bits of n
as a counter:
double rec_sqrt_series(int n)
{
static const int R = 0x10000;
return n/R < n%R ? sqrt(n/R+1 + rec_sqrt_series(n+R)) : 0;
}
Naturally, that malfunctions when the initial n
is R
or greater. Here is a more complicated version that works for any positive value of n
. It works:
- When
n
is negative, it works like the above version, using the upper bits to count. - When
n
is positive, if it is less thanR
, it calls itself with-n
to evaluate the function as above. Otherwise, it calls itself withR-1
negated. This evaluates the function as if it were called withR-1
. This produces the correct result because the series stops changing in the floating-point format after just a few dozen iterations—the square roots of the deeper numbers get so diluted they have no effect. So the function has the same value for alln
over a small threshold.
double rec_sqrt_series(int n)
{
static const int R = 0x100;
return
0 < n ? n < R ? rec_sqrt_series(-n) : rec_sqrt_series(1-R)
: n/R > n%R ? sqrt(-n/R+1 + rec_sqrt_series(n-R)) : 0;
}
回答2:
Without mathematically transforming the formula (I don't know if it is possible), you can't truly use just one parameter, as for each element you need two informations: the current step and the original n
. However you can cheat. One way is to encode the two numbers in the int
parameter (as shown by Eric).
Another way is to store the original n
in a static local variable. At the first call we save n
in this static variable, we start the recursion and at the last step we reset it to the sentinel value:
// fn(i) = sqrt(n + 1 - i + fn(i - 1))
// fn(1) = sqrt(n)
//
// note: has global state
double f(int i)
{
static const int sentinel = -1;
static int n = sentinel;
// outside call
if (n == sentinel)
{
n = i;
return f(n);
}
// last step
if (i == 1)
{
double r = sqrt(n);
n = sentinel;
return r;
}
return sqrt(n + 1 - i + f(i - 1));
}
Apparently static int n = sentinel
is not standard C because sentinel
is not a compile time constant in C (it is weird because both gcc and clang don't complain, even with -pedantic
)
You can do this instead:
enum Special { Sentinel = -1 };
static int n = Sentinel;
回答3:
This problem begs for contorted solutions.
Here is one that uses a single function taking one or two int
arguments:
- if the first argument is positive, it computes the expression for that value
- if the first argument is negative, it must be followed by a second argument and performs a single step in the computation, recursing for the previous step.
- it uses
<stdarg.h>
which might or might not be allowed.
Here is the code:
#include <math.h>
#include <stdarg.h>
double rec_sqrt_series(int n, ...) {
if (n < 0) {
va_arg ap;
va_start(ap, n);
int m = va_arg(ap, int);
va_end(ap);
if (m > -n) {
return 0.0;
} else {
return sqrt(m + rec_sqrt_series(n, m + 1));
}
} else {
return rec_sqrt_series(-n, 1);
}
}
Here is another solution with a single function, using only <math.h>
, but abusing the rules in a different way: using a macro.
#include <math.h>
#define rec_sqrt_series(n) (rec_sqrt_series)(n, 1)
double (rec_sqrt_series)(int n, int m) {
if (m > n) {
return 0.0;
} else {
return sqrt(m + (rec_sqrt_series)(n, m + 1));
}
}
Yet another one, strictly speaking recursive, but with single recursion level and no other tricks. As Eric commented, it uses a for
loop which might be invalid under the OP's constraints:
double rec_sqrt_series(int n) {
if (n > 0) {
return rec_sqrt_series(-n);
} else {
double x = 0.0;
for (int i = -n; i > 0; i--) {
x = sqrt(i + x);
}
return x;
}
}
回答4:
Here is another approach.
It relies on int
being 32 bits. The idea is to use the upper 32 bit of a 64 bit int
to
1) See if the call was a recursive call (or a call from the "outside")
2) Save the target value in the upper 32 bits during recursion
// Calling convention:
// when calling this function 'n' must be a positive 32 bit integer value
// If 'n' is zero or less than zero the function have undefined behavior
double rec_sqrt_series(uint64_t n)
{
if ((n >> 32) == 0)
{
// Not called by a recursive call
// so start the recursion
return rec_sqrt_series((n << 32) + 1);
}
// Called by a recursive call
uint64_t rn = n & 0xffffffffU;
if (rn == (n >> 32)) return sqrt(rn); // Done - target reached
return sqrt (rn + rec_sqrt_series(n+1)); // Do the recursive call
}
来源:https://stackoverflow.com/questions/61031584/calculating-a-nested-root-in-c