Bir çift özyinelemeli işlevin çalışma zamanını nasıl belirlerim?


15

Herhangi bir keyfi olarak çift özyinelemeli işlev verildiğinde, çalışma süresi nasıl hesaplanır?

Örneğin (sözde kodda):

int a(int x){
  if (x < = 0)
    return 1010;
  else
    return b(x-1) + a(x-1);
}
int b(int y){
  if (y <= -5)
    return -2;
  else
    return b(a(y-1));
}

Ya da bu çizgiler boyunca bir şey.

Böyle bir şeyi belirlemek için hangi yöntemler kullanılabilir veya kullanılmalıdır?


2
Bu ödev mi?
Bernard

5
Hayır, yaz zamanı ve öğrenmeyi seviyorum. Beynimin lapa dönüşmesine izin vermek yerine ilerleyeceğim.
if_zero_equals_one

11
Tamam anladım. Bunu Stack Overflow'a taşımak için oy verenlere: bu konu üzerinde ve Stack Overflow'da konu dışı. Programcılar, kavramsal, beyaz tahta-y soruları içindir; Stack Overflow kodlama soruları için uygulama, sorun ise.

3
Teşekkürler ilk etapta burada yapmamın nedeni budur. Ayrıca nasıl balık tutulacağını bilmek balık almaktan daha iyidir.
if_zero_equals_one

1
Bu özel durumda, yine de genellikle sonsuz bir özyinelemedir, çünkü b (a (0)) sonsuz sayıda başka b (a (0)) terimini çağırır. Bir matematik formülü olsaydı farklı olurdu. Kurulumunuz farklı olsaydı, farklı bir şekilde çalışırdı. Matematikte olduğu gibi, cs'de bazı problemlerin bir çözümü var, bazıları yok, bazıları kolay bir tane var, bazıları yok. Çözümün var olduğu karşılıklı olarak tekrarlanan birçok durum vardır. Bazen bir istifin üflenmemesi için trambolin paterni kullanılması gerekir.
İş

Yanıtlar:


11

Siz işlevinizi değiştirmeye devam edersiniz. Ama dönüşüm olmadan sonsuza dek çalışacakları seçmeye devam et

Özyineleme karmaşık ve hızlı hale gelir. Önerilen iki kat özyinelemeli işlevi analiz etmenin ilk adımı, onu bazı örnek değerler üzerinde izlemeye çalışmak ve ne yaptığını görmek. Hesaplamanız sonsuz bir döngüye girerse, işlev iyi tanımlanmamıştır. Hesaplamanız daha büyük rakamlar almaya devam eden bir sarmal haline gelirse (ki bu çok kolay olur), muhtemelen iyi tanımlanmamıştır.

Eğer onu takip etmek bir cevap verirse, cevaplar arasında bir örüntü veya tekrarlama ilişkisi bulmaya çalışırsınız. Bunu yaptıktan sonra, çalışma zamanını anlamaya çalışabilirsiniz. Bunu anlamak çok, çok karmaşık olabilir, ancak birçok durumda cevabı bulmamızı sağlayan Master teoremi gibi sonuçlarımız var .

Tek özyineleme ile bile, çalışma zamanını nasıl hesaplayacağımızı bilmediğimiz işlevlerle gelmenin kolay olduğunu unutmayın. Örneğin aşağıdakileri göz önünde bulundurun:

def recursive (n):
    if 0 == n%2:
        return 1 + recursive(n/2)
    elif 1 == n:
        return 0
    else:
        return recursive(3*n + 1)

Öyle anda bilinmeyen bu işlev her zaman çalışma zamanı ne dursun, iyi tanımlanmış olup olmadığını.


5

Söz konusu işlev çiftinin çalışma zamanı sınırsızdır, çünkü ikisi de diğerini çağırmadan geri dönmez. Dönüş değeri aolan her zaman yapılan çağrının dönüş değerine bağımlı bolan her zaman çağırır a... ve olarak bilinen yıllardan bu sonsuz özyineleme .


Burada belirli işlevleri aramamak. Birbirini çağıran özyinelemeli işlevlerin çalışma süresini bulmak için genel bir yol arıyorum.
if_zero_equals_one

1
Genel durumda bir çözüm olduğundan emin değilim. Big-O'nun mantıklı olması için algoritmanın durup durmayacağını bilmelisiniz. Ne kadar süreceğini bilmeden önce hesaplamayı çalıştırmanız gereken bazı özyinelemeli algoritmalar vardır (örneğin, bir noktanın Mandlebrot kümesine ait olup olmadığını belirleme).
jimreed

Her zaman değil, ayalnızca biletilen numara> = 0 ise çağırır. Ancak evet, sonsuz bir döngü var.
btilly

1
@btilly cevabımı gönderdikten sonra örnek değiştirildi.
jimreed

1
@jimreed: Ve tekrar değiştirildi. Yapabilseydim yorumumu silerdim.
btilly

4

Bariz yöntem işlevi çalıştırmak ve ne kadar sürdüğünü ölçmektir. Ancak bu yalnızca belirli bir girdinin ne kadar sürdüğünü gösterir. Önceden işlevin sonlandığını bilmiyorsanız, zor: işlevin sonlanıp sonlanmadığını anlamanın mekanik bir yolu yoktur - bu durma problemidir ve karar verilemez.

Bir fonksiyonun çalışma süresini bulmak, Rice teoremi ile benzer şekilde kararlaştırılamaz . Aslında Rice'ın teoremi, bir fonksiyonun O(f(n))zamanında çalışıp çalışmadığına karar vermenin bile kararsız olduğunu gösterir.

Bu yüzden genel olarak yapabileceğiniz en iyi şey, bildiğimiz kadarıyla Turing makinelerinin sınırlarına bağlı olmayan insan zekanızı kullanmak ve bir deseni tanımaya veya bir icat yapmaya çalışmaktır. Bir işlevin çalışma süresini analiz etmenin tipik bir yolu, işlevin özyinelemeli tanımını, çalışma zamanında özyinelemeli bir denkleme (veya karşılıklı olarak özyinelemeli işlevler için bir dizi denkleme) dönüştürmektir:

T_a(x) = if x ≤ 0 then 1 else T_b(x-1) + T_a(x-1)
T_b(x) = if x ≤ -5 then 1 else T_b(T_a(x-1))

Sırada ne var? Şimdi bir matematik probleminiz var: bu fonksiyonel denklemleri çözmeniz gerekiyor. Genellikle çalışan bir yaklaşım fonksiyonları yorumlayarak, bu çözmek için analitik fonksiyonlar ve kullanım calculus'a denklem içine tamsayı fonksiyonları üzerine bu denklemleri açmaktır T_ave T_bolarak üreten fonksiyonlar .

Fonksiyonlar ve diğer ayrık matematik konularında Ronald Graham, Donald Knuth ve Oren Patashnik'in Beton Matematik kitabını öneriyorum .


1

Diğerlerinin belirttiği gibi, özyinelemeyi analiz etmek çok hızlı bir şekilde zorlaşabilir. İşte böyle bir şeyin başka bir örneği: http://rosettacode.org/wiki/Mutual_recursion http://en.wikipedia.org/wiki/Hofstadter_sequence#Hofstadter_Female_and_Male_sequences Bir cevap ve çalışma süresi hesaplamak zor. Bunun nedeni, "zor bir forma" sahip olan karşılıklı olarak özyinelemeli işlevlerdir.

Her neyse, bu kolay örneğe bakalım:

http://pramode.net/clojure/2010/05/08/clojure-trampoline/

(declare funa funb)
(defn funa [n]
  (if (= n 0)
    0
    (funb (dec n))))
(defn funb [n]
  (if (= n 0)
    0
    (funa (dec n))))

Hesaplamaya çalışarak başlayalım funa(m), m > 0:

funa(m) = funb(m - 1) = funa(m - 2) = ... funa(0) or funb(0) = 0 either way.

Çalışma süresi:

R(funa(m)) = 1 + R(funb(m - 1)) = 2 + R(funa(m - 2)) = ... m + R(funa(0)) or m + R(funb(0)) = m + 1 steps either way

Şimdi biraz daha karmaşık bir örnek daha seçelim:

Kendi başına iyi bir okuma olan http://planetmath.org/encyclopedia/MutualRecursion.html'den esinlenerek şunları inceleyelim: "" "Fibonacci sayıları karşılıklı özyineleme ile yorumlanabilir: F (0) = 1 ve G (0) ) = 1, F (n + 1) = F (n) + G (n) ve G (n + 1) = F (n) ile. "" "

Peki, F'nin çalışma süresi nedir? Diğer yoldan gideceğiz.
Şey, R (F (0)) = 1 = F (0); R (G (0)) = 1 = G (0)
Şimdi R (F (1)) = R (F (0)) + R (G (0)) = F (0) + G (0) = F (1)
...
R (F (m)) = F (m) - örneğin, i indeksindeki bir Fibonacci numarasını hesaplamak için gereken fonksiyon çağrılarının sayısının bir Fibonacci sayısının değerine eşit olduğunu görmek zor değil i dizininde. Bu, iki sayı birlikte eklemenin bir işlev çağrısından çok daha hızlı olduğu varsayılmıştır. Durum böyle olmasaydı, bu doğru olurdu: R (F (1)) = R (F (0)) + 1 + R (G (0)) ve bunun analizi daha karmaşık olurdu, muhtemelen kolay kapalı bir form çözümü olmadan.

Fibonacci dizisi için kapalı formun yeniden icat edilmesi kolay değildir, daha karmaşık örneklerden bahsetmiyoruz.


0

Yapılacak ilk şey, tanımladığınız işlevlerin tam olarak hangi değerlere sonlandığını göstermektir. Tanımladığınız örnekte

int a(int x){
  if (x < = 0)
    return 1010;
  else
    return b(x-1) + a(x-1);
}
int b(int y){
  if (y <= -5)
    return -2;
  else
    return b(a(y-1));
}

by <= -5çünkü yalnızca başka bir değer eklerseniz formun bir terimi olacaktır b(a(y-1)). Eğer genişleyen biraz daha yaparsanız göreceksiniz formun bir terim olduğunu b(a(y-1))terime sonunda açar b(1010)hangi bir terim için iletkenler b(a(1009))hangi terime tekrar açar b(1010). Bu, atatmin edici olmayan herhangi bir değeri ekleyemeyeceğiniz anlamına gelir, x <= -4çünkü bunu yaparsanız, hesaplanacak değerin hesaplanacak değere bağlı olduğu sonsuz bir döngü elde edersiniz. Yani bu örnekte sabit çalışma süresi vardır.

Bu nedenle basit cevap, özyinelemeli işlevlerin çalışma süresini belirlemek için genel bir yöntem olmamasıdır, çünkü özyinelemeli olarak tanımlanmış bir işlevin sonlanıp sonlanmadığını belirleyen genel bir prosedür yoktur.


-5

Big-O'da olduğu gibi mi?

Bu kolay: O (N) - bir fesih koşulu olduğu varsayılarak.

Özyineleme sadece döngüdür ve bu döngüde ne kadar çok şey yaparsanız yapın (ve başka bir yöntem çağırmak döngüdeki başka bir adımdır ) basit bir döngü O (N ) 'dir.

İlginç olacağı nokta, özyinelemeli yöntemlerden bir veya daha fazlasında bir döngü varsa. Bu durumda, bir tür üstel performans elde edersiniz ( yöntemden her geçişte O (N) ile çarpılır ).


2
Big-O performansını, çağrılan herhangi bir yöntemin en yüksek sırasını alıp çağıran yöntemin sırasıyla çarparak belirlersiniz. Ancak, üstel ve faktöryel performans hakkında konuşmaya başladığınızda, polinom performansını göz ardı edebilirsiniz. Ben inanıyorum faktöryel kazanır: üstel ve faktöriyel karşılaştırırken aynı olduğu anlamına gelmemektedir. Asla hem üstel hem de faktöriyel bir sistemi analiz etmek zorunda kalmadım.
Anon

5
Bu yanlış. Quicksort n'inci Fibonacci sayısını hesaplamak ve yinelemeli formlarıdır O(2^n)ve O(n*log(n))sırasıyla.
unpythonic

1
Bazı fantezi kanıtlar yapmadan sizi amazon.com/Introduction-Algorithms-Second-Thomas-Cormen/dp/… adresine yönlendirmek ve cstheory.stackexchange.com adresinden bir göz atmayı denemek istiyorum .
Bryan Harrington

4
İnsanlar neden bu korkunç yanıtı oyladılar? Bir yöntemin çağrılması, yöntemin aldığı zamanla orantılı olarak zaman alır. Bu durumda yöntem açağrıları bve baramaları abu kadar olamaz sadece iki yöntem zaman aldığını varsayalım O(1).
btilly

2
@Anon - Poster, yalnızca yukarıda gösterileni değil, keyfi olarak çift yinelemeli bir işlev istiyordu. Açıklamanıza uymayan iki basit özyineleme örneği verdim. Eski standartları, üstel (uyarınıza uyan) ve kapalı olmayan (kapalı olmayan) bir "çift özyinelemeli" forma dönüştürmek önemsizdir.
unpythonic
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.