Вычисление вложенного root в C - PullRequest
9 голосов
/ 04 апреля 2020

Меня попросили вычислить следующее вложенное root выражение, используя рекурсию * только 1002 *.

enter image description here

Я написал код ниже это работает, но они позволили нам использовать только одну функцию и 1 вход n для цели, а не 2, как я использовал. Может кто-нибудь помочь мне преобразовать этот код в одну функцию, которая будет вычислять выражение? Нельзя использовать любую библиотеку, кроме функций из <math.h>.

для вывода при 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);
}

Ответы [ 4 ]

7 голосов
/ 04 апреля 2020

Используйте старшие биты n в качестве счетчика:

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;
}

Естественно, это неисправности, когда начальный n равен R или больше. Вот более сложная версия, которая работает для любого положительного значения n. Он работает:

  • Когда n отрицателен, он работает как вышеприведенная версия, используя верхние биты для подсчета.
  • Когда n положителен, если он меньше чем R, он вызывает себя с -n для оценки функции, как указано выше. В противном случае он вызывает себя с отрицанием R-1. Это оценивает функцию, как если бы она была вызвана с R-1. Это дает правильный результат, потому что ряд перестает изменяться в формате с плавающей запятой после нескольких десятков итераций - квадратные корни более глубоких чисел настолько разбавляются, что не имеют никакого эффекта. Таким образом, функция имеет одинаковое значение для всех n через небольшой порог.
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;
}
5 голосов
/ 04 апреля 2020

Без математического преобразования формулы (я не знаю, возможно ли это), вы не сможете по-настоящему использовать только один параметр, так как для каждого элемента вам нужны две информации: текущий шаг и исходный n. Однако вы можете обмануть . Одним из способов является кодирование двух чисел в параметре int (как показано Eri c).

Другим способом является сохранение оригинала n в состоянии c локальная переменная. При первом вызове мы сохраняем n в этой переменной * stati c, запускаем рекурсию и на последнем шаге сбрасываем ее в значение Sentinel:

// 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));
}

Видимо static int n = sentinel не является стандартным C, потому что sentinel не является постоянной времени компиляции в C (это странно, потому что и g cc, и clang не жалуются, даже с -pedantic)

You можно сделать это вместо:

enum Special { Sentinel = -1 };
static int n = Sentinel;
5 голосов
/ 04 апреля 2020

Эта проблема напрашивается для искаженных решений.

Вот та, которая использует одну функцию, принимающую один или два int аргумента:

  • , если первый аргумент положительный, он вычисляет выражение для этого значения
  • , если первый аргумент отрицательный, за ним должен следовать второй аргумент и выполняется один шаг в вычислении, повторяющийся для предыдущего шага.
  • , который он использует <stdarg.h>, который может или не может быть разрешен.

Вот код:

#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);
    }
}

Вот еще одно решение с одной функцией, использующее только <math.h>, но злоупотребление правилами по-другому: использование макроса.

#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));
    }
}

Еще один, строго говоря рекурсивный , но с одним уровнем рекурсии и без других уловок. Как прокомментировал Eri c, он использует for l oop, который может быть недопустимым при ограничениях OP:

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;
    }
}
0 голосов
/ 05 апреля 2020

Вот еще один подход.

Он опирается на int 32 бит. Идея состоит в том, чтобы использовать верхний 32-битный 64-битный int до

1) Посмотрите, был ли вызов рекурсивным (или вызовом извне)

2 ) Сохраните целевое значение в старших 32 битах во время рекурсии

// 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
}
...