C ile iç içe bir kök hesaplama


9

Sadece iç içe geçmiş kök ifadesini yalnızca özyineleme kullanarak hesaplamak istendi .

resim açıklamasını buraya girin

Aşağıda işe yarayan kodu yazdım, ama sadece bir işlevi ve 1 girişi kullanmamıza izin nverdim, kullandığım gibi 2 değil. Birisi bu kodu ifadeyi hesaplayacak bir işleve dönüştürmeme yardımcı olabilir mi? işlevler dışında herhangi bir kitaplığı kullanamazsınız <math.h>.

n = 10 için çıkış: 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);
}

Birisi bu kodu ifadeyi hesaplayacak bir işleve dönüştürmeme yardımcı olabilir mi? ne? Kurtulmak için yardım helper?
4386427

Eğer argümanlar yanlış ise, ben abort()(from <stdlib.h>), sessizce 0 dönmek değil çağırır.
Kaz

1
@chqrlieforyellowblockquotes Twisied. @pastaleg Yararsız özyineleme nasıl olur? 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 - Monica adlı kişiyi geri yükle

1
@ chux-ReinstateMonica: evet, kuralları daha basit kötüye kullanma.
chqrlie

2
@Oppen: Görevin amacı, fonksiyonun özyinelemeli olmayan bir ifadesini finanse etmek olsaydı, muhtemelen sorunun “yalnızca özyineleme” kullanılarak çözülmesini istemezdi. Kesinlikle basit bir döngü sonucu kolayca hesaplar. Bu sorular ödevin gerçek metni olmadan Stack Overflow'a gönderildiğinde genellikle şüpheli olsam da.
Eric Postpischil

Yanıtlar:


7

Öğesinin üst bitlerini nsayaç olarak kullanı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;
}

Doğal olarak, ilk bu arızalar zaman nolan Rya da daha büyüktür. İşte herhangi bir pozitif değeri için çalışan daha karmaşık bir versiyon n. İşe yarıyor:

  • nNegatif olduğunda , saymak için üst bitleri kullanarak yukarıdaki sürüm gibi çalışır.
  • Zaman npozitiftir bu daha az ise, Rbu kendisini aramalar -nyukarıdaki gibi fonksiyonunu değerlendirmek için. Aksi takdirde, kendisini R-1reddederek çağırır . Bu, işlevi çağrılmış gibi değerlendirir R-1. Bu doğru sonucu verir, çünkü seri sadece birkaç düzine yinelemeden sonra kayan nokta biçiminde değişmeyi durdurur - daha derin sayıların kare kökleri o kadar seyreltilir ki hiçbir etkisi yoktur. Böylece fonksiyon nküçük bir eşiğin her biri için aynı değere sahiptir .
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;
}

İyi fikir, ama 32-bit ints varsayar :)
chqrlie

1
@chqrlieforyellowblockquotes: Hayır, bu yüzden Rayrı, bu yüzden ayarlanabilir. n32'ye ulaşmadan önce , IEEE-754 binary64 için dönüş değeri değişmez ve 256'ya ulaşmadan önce dönüş değeri makul biçimler için değişmez double. Bu yüzden yukarıdaki kelepçe girişlerini dönüştüren alternatif bir sürümü düşünüyorum R, ancak işaret bitini kullanması gerekiyor ve hala üzerinde çalışıyorum.
Eric Postpischil

Kullanabileceğiniz başka eşleştirme işlevleri de vardır, ancak hiçbiri sizinki kadar basit değildir. Ana avantajları tipik olarak keyfi hassasiyetle çalışmalarıdır, ancak OP bundan böyle bir gereklilik olarak asla bahsetmedi.
Ruud Helderman

@chqrlieforyellowblockquotes: Tamamlandı. Şimdi ngenişliği ne olursa olsun herhangi bir pozitif için doğru cevap üretir int.
Eric Postpischil

5

Formülü matematiksel olarak dönüştürmeden (mümkün olup olmadığını bilmiyorum), her bir öğe için iki bilgiye ihtiyacınız olduğu için sadece bir parametre kullanamazsınız: mevcut adım ve orijinal n. Ancak hile yapabilirsiniz . Bunun bir yolu intparametredeki iki sayıyı kodlamaktır ( Eric tarafından gösterildiği gibi ).

Başka bir yol, orijinali nstatik bir yerel değişkende saklamaktır . nBu statik değişkene kaydettiğimiz ilk çağrıda , özyinelemeyi başlatırız ve son adımda onu sentinel değerine sıfırlarız:

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

Görünüşe göre static int n = sentinelstandart C değildir çünkü C'de sentinelbir derleme zaman sabiti değildir (hem gcc hem de clang ile şikayetçi olmadığı için gariptir -pedantic)

Bunun yerine şunları yapabilirsiniz:

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

İlginç bir yaklaşım, ancak korkarım başlatıcı static int n = sentinel;C'de tam olarak uyumlu değildir, çünkü sentinelC Standardına göre sabit bir ifade değildir. Bu C ++ çalışıyor ve gcc ve clang güncel sürümleri MSVC 2017 C modunda değil ile derler, ama muhtemelen yazma gerektiğini static int n = -1;bkz godbolt.org/z/8pEMnz
chqrlie

1
(@chqrlieforyellowblockquotes) Bunu işaret ettiğiniz için teşekkür ederim. İlginç derleyici davranışı. Bu soruda bunu sordum: stackoverflow.com/q/61037093/2805305
bolov

5

Bu sorun, çarpık çözümler için yalvarır.

Bir veya iki intargüman alan tek bir işlevi kullanan bir örnek :

  • ilk argüman pozitifse, o değerin ifadesini hesaplar
  • ilk argüman negatifse, bunu ikinci bir argüman izlemeli ve hesaplamada bir önceki adım için yinelenen tek bir adım gerçekleştirmelidir.
  • <stdarg.h>izin verilip verilemeyeceğini kullanır .

İşte kod:

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

İşte tek bir işlevi olan, sadece kullanan <math.h>, ancak kuralları farklı bir şekilde kötüye kullanan başka bir çözüm : bir makro kullanma.

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

Yine başka bir, kesinlikle konuşma özyinelemeli , ancak tek özyineleme seviyesi ve başka hileler ile. As Eric yorumladı, bir kullanır forOP'ın kısıtlar altında geçersiz olabilir döngü:

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

evet işe yarıyor sanırım. tüm yardım için çok teşekkür ederim
Ronen Dvorkin

Son olarak double rec_sqrt_series(int n), IMO, işareti özyineleme bayrağı olarak kullanarak OP'nin hedeflerine ulaşır. (Ben düşeceği elsegibi gerekli değildir returniçindedir if.)
chux - Eski Monica

1
@ chux-ReinstateMonica: elseTabii ki bırakmak mümkündür ama ifsonuç bir tür dönen her iki dalı simetri gibi bir tür fonksiyonel programlama tarzı.
chqrlie

@ chux-ReinstateMonica: Ödevin “yalnızca özyineleme” gereksiniminin yinelemeyi engellemesini bekliyorum.
Eric Postpischil

@EricPostpischil: evet, ben de aynı şeyi düşündüm, ama OP'den geri bildirim alamadım.
chqrlie

0

İşte başka bir yaklaşım.

int32 bit olmaya dayanır . Fikir, 64 bitlik üst 32 biti kullanmaktır int.

1) Çağrının yinelemeli çağrı mı (yoksa "dışarıdan" yapılan çağrı mı) bakın

2) Özyineleme sırasında hedef değeri üst 32 bite kaydedin

// 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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.