C에서 내포 된 루트 계산


9

재귀 만 사용하여 다음 중첩 루트 식을 계산하라는 요청을 받았습니다 .

여기에 이미지 설명을 입력하십시오

나는 아래 코드를 작성했지만 효과가 있었기 때문에 하나의 함수하나의 입력 만 사용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);
}

누군가이 코드를 표현식을 계산하는 하나의 함수로 변환하도록 도울 수 있습니까? 뭐? 당신이 제거하는 데 도움이 helper?
4386427

인수가 틀리면 abort()(부터 <stdlib.h>)를 호출하고 자동으로 0을 반환하지 않습니다.
Kaz

1
@chqrlieforyellowblockquotes 트위스트되었습니다. @pastaleg 쓸모없는 재귀는 어떻습니까? double nested_root(unsigned n) { double x = 0.0; if (n > 0) { x = nested_root(0); for (unsigned i = n; i > 0; i--) { x = sqrt(i + x); } } return x; }
chux

1
@ chux-ReinstateMonica : 예, 규칙을 더 간단하게 남용합니다.
chqrlie

2
@Oppen : 과제의 목표가 비 재귀적인 함수 표현에 자금을 지원하는 것이라면 아마도 "재귀 만"을 사용하여 문제를 해결하도록 요구하지는 않을 것입니다. 확실히 간단한 루프는 결과를 쉽게 계산할 것입니다. 이러한 질문이 과제의 실제 텍스트없이 스택 오버플로에 게시 될 때 일반적으로 의심 스럽습니다.
Eric Postpischil

답변:


7

의 상위 비트를 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;
}

당연히, 초기 nR이상일 때 오작동합니다 . 다음은 양수 값으로 작동하는보다 복잡한 버전입니다 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;
}

좋은 생각이지만 32 비트 정수를 가정합니다 :)
chqrlie

1
@chqrlieforyellowblockquotes : 아 R, 그렇기 때문에 별개이므로 조정할 수 있습니다. n32에 도달 하기 전에 IEEE-754 binary64에 대한 반환 값 변경이 중지되고 256에 도달하기 전에에 적합한 형식의 반환 값 변경이 중지됩니다 double. 따라서 위의 클램프 입력을 변환하는 대체 버전을 고려하고 R있지만 부호 비트를 사용해야하며 여전히 작업 중입니다.
Eric Postpischil

사용할 수 있는 다른 페어링 기능 이 있지만 사용자만큼 간단한 기능 은 없습니다. 주요 장점은 일반적으로 임의의 정밀도로 작동한다는 점이지만 OP는이를 요구 사항으로 언급하지 않았습니다.
Ruud Helderman

@chqrlieforyellowblockquotes : 완료 이제 n너비에 관계없이 모든 양수에 대한 정답을 산출합니다 int.
Eric Postpischil

5

수학적으로 수식을 변환하지 않으면 (가능한지 모르겠습니다) 각 요소에 대해 현재 단계와 원래의 두 가지 정보가 필요한 것과 같이 하나의 매개 변수 만 사용할 수는 없습니다 n. 그러나 당신은 속일 수 있습니다 . 한 가지 방법은 int매개 변수 에서 두 숫자를 인코딩하는 것입니다 ( Eric에서 표시 ).

또 다른 방법은 원본 n을 정적 로컬 변수에 저장하는 것 입니다. 첫 번째 호출 n에서이 정적 변수를 저장 하면 재귀가 시작되고 마지막 단계 에서이 값을 센티넬 값으로 재설정합니다.

// 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 = sentinelC sentinel의 컴파일 시간 상수가 아니기 때문에 분명히 표준 C 가 아닙니다 (gcc와 clang이 모두조차도 불평하지 않기 때문에 이상합니다 -pedantic)

대신이 작업을 수행 할 수 있습니다.

enum Special { Sentinel = -1 };
static int n = Sentinel;

흥미로운 접근 방법이지만 C 표준에 따라 상수식이 static int n = sentinel;아니기 때문에 이니셜 라이저 가 C에서 완전히 호환되지 않을까 걱정 sentinel됩니다. 그것은 ++ C에서 작동, 그리고 GCC와 그 소리의 최신 버전 MSVC 2017 C 모드에서가 아니라 함께 컴파일하지만 당신은 아마 작성해야 static int n = -1;godbolt.org/z/8pEMnz
chqrlie을

1
@chqrlieforyellowblockquotes ish. 이것을 지적 해 주셔서 감사합니다. 흥미로운 컴파일러 동작. 나는이 질문에 이것에 대해 물었다 : stackoverflow.com/q/61037093/2805305
bolov

5

이 문제는 뒤틀린 솔루션을 요구합니다.

다음은 하나 또는 두 개의 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));
    }
}

또 다른 하나는 엄격하게 재귀 적이 지만 단일 재귀 수준과 다른 트릭은 없습니다. Eric이 언급 했듯이 forOP의 제약 조건에서는 유효하지 않은 루프를 사용합니다 .

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

그래, 내가 작동하는 것 같아요. 모든 도움을 주셔서 대단히 감사합니다
Ronen Dvorkin

마지막 double rec_sqrt_series(int n)으로 IMO는 부호를 재귀 플래그로 사용하여 OP의 목표를 달성합니다. (나는 드롭 것 else뿐만 필요하지를 return입니다 if.)
chux - 분석 재개 모니카

1
@ chux-ReinstateMonica : else물론 떨어 뜨릴 수는 있지만 if기능 프로그래밍 스타일의 결과를 반환하는 두 가지 가지의 대칭을 좋아합니다 .
chqrlie

@ chux-ReinstateMonica : 과제의“재귀 전용”요구 사항이 반복을 방해한다고 생각합니다.
Eric Postpischil

@EricPostpischil : 예, 같은 생각을했지만 OP로부터 피드백을받지 못했습니다.
chqrlie

0

다른 접근법이 있습니다.

그것은 int32 비트 에 의존합니다 . 아이디어는 64 비트의 상위 32 비트를 사용하는 것이다 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
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.