Cでのネストされたルートの計算


9

再帰のみを使用して、次のネストされたルート式を計算するように求められました。

ここに画像の説明を入力してください

機能する以下のコードを作成しnましたが、目的に応じて1 つの関数と1 つの入力しか使用できず、以前のように2 つ使用することはできませんでした。誰かがこのコードを式を計算する1つの関数に変換するのを手伝ってくれませんか?の関数以外のライブラリは使用できません<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);
}

誰かがこのコードを式を計算する1つの関数に変換するのを手伝ってくれませんか?何?を取り除くのに役立ちhelperますか?
4386427

引数が間違っている場合は、私が呼ぶだろうabort()(から<stdlib.h>)、静かに0を返さない
カズ

1
@chqrlieforyellowblockquotes Twisied。@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;
}

当然、イニシャル以上で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;
}

良い考えですが、32ビット整数を想定しています:)
chqrlie

1
@chqrlieforyellowblockquotes:いやR、それが分離されているので、調整することができます。n32に達する前に、IEEE-754 binary64の戻り値は変更を停止し、256に達する前に、戻り値はの適切な形式で変更を停止しますdouble。したがってR、上記のクランプ入力を変換する別のバージョンを検討していますが、符号ビットを使用する必要があり、まだ作業中です。
Eric Postpischil

使用できる他のペアリング関数がありますが、あなたほど簡単なものはありません。それらの主な利点は通常、任意の精度で動作することですが、OPはそれを要件として言及していません。
Ruud Helderman

@chqrlieforyellowblockquotes:完了しました。これnで、の幅に関係なく、すべての正の正解が生成されますint
Eric Postpischil

5

式を数学的に変換しないと(可能かどうかはわかりません)、各要素には現在のステップと元のステップの2つの情報が必要なので、実際には1つのパラメーターだけを使用することはできませんn。しかし、あなたはカンニングすることができます。1つの方法は、intパラメーターに2つの数値をエンコードすることです(Ericが示すように)。

もう1つの方法は、オリジナル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 = sentinelので、標準のCではありませんsentinelCでのコンパイル時定数ではありません(gccと打ち鳴らすの両方がさえて、文句を言わないので、それは奇妙です-pedantic

代わりにこれを行うことができます:

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

興味深いアプローチですが、イニシャライザstatic int n = sentinel;sentinelC標準に準拠した定数式ではないため、Cでは完全に準拠していません。これはC ++で動作し、Cモードではgccおよびclangの現在のバージョンでコンパイルされますが、MSVC 2017ではコンパイルされませんが、おそらくgodbolt.org/z/8pEMnzstatic int n = -1;参照してください
chqrlie

1
@chqrlieforyellowblockquotes ish これを指摘していただきありがとうございます。興味深いコンパイラの動作。私はこの質問でこれについて尋ねました:stackoverflow.com/q/61037093/2805305
bolov

5

この問題は、歪んだ解決策を求めています。

以下は、1つまたは2つのint引数を取る単一の関数を使用するものです。

  • 最初の引数が正の場合、その値の式を計算します
  • 最初の引数が負の場合は、2番目の引数が後に続き、計算で1つのステップを実行して、前のステップを繰り返します。
  • <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));
    }
}

さらにもう1つ、厳密に言えば再帰的ですが、再帰レベルは1つで、他のトリックはありません。エリックはコメントし、それは使用しています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ように必要ないのでドロップreturnifます。)
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.