2015-03-05 15 views
5

Stworzyłem zapamiętaną funkcję rekurencyjnej wersji fibonacci. Używam tego jako przykładu dla innych rodzajów funkcji, które używają notowania. Moja implementacja jest złe, ponieważ jeśli umieścić go w bibliotece, co oznacza, że ​​zmienna global jest nadal postrzegane ..Funkcja memoizing fibonacci w php

Jest to oryginalna rekurencyjna funkcja Fibonacci:

function fibonacci($n) { 
    if($n > 1) { 
    return fibonacci($n-1) + fibonacci($n-2); 
    } 
    return $n; 
} 

i modyfikować go do wersja zapamiętana:

$memo = array(); 
function fibonacciMemo($n) { 
    global $memo; 
    if(array_key_exists($n, $memo)) { 
    return $memo[$n]; 
    } 
    else { 
    if($n > 1) { 
     $result = fibonacciMemo($n-1) + fibonacciMemo($n-2); 
     $memo[$n] = $result; 
     return $result; 
    } 
    return $n; 
    } 
} 

Celowo nie użyłem metody iteracyjnej w implementacji fibonacci. Czy istnieją lepsze sposoby na zapamiętanie funkcji fibonacci w php? Czy możesz zaproponować mi lepsze ulepszenia? Widziałem func_get_args() i call_user_func_array jako inny sposób, ale nie mogę wydawać się wiedzieć, co jest lepsze?

Moje główne pytanie brzmi: Jak mogę poprawnie zapamiętać funkcję fibonacci w php? lub Jaki jest najlepszy sposób na zapamiętanie funkcji fibonacci w php?

+0

przechodzącego '$ notatka "jako parametr' fibonacciMemo'? chociaż jest znacznie mniej elegancki :( –

+0

cóż, myślę, że to również możliwe, ale to, czego szukam, jest jak dotąd najlepszą implementacją tej funkcji .. :) – catzilla

+0

Zobacz [memoised] (https: // github .com/ihor/Nspl # memoizedfunction) funkcja z [Nspl] (https: // github.com/ihor/Nspl) –

Odpowiedz

5

Cóż, Edd Mann pokazuje doskonały sposób zaimplementować memoize funkcję w php w His post

Oto przykładowy kod (w rzeczywistości pochodzi z postu EDD Manna):

$memoize = function($func) 
{ 
    return function() use ($func) 
    { 
     static $cache = []; 

     $args = func_get_args(); 

     $key = md5(serialize($args)); 

     if (! isset($cache[$key])) { 
      $cache[$key] = call_user_func_array($func, $args); 
     } 

     return $cache[$key]; 
    }; 
}; 

$fibonacci = $memoize(function($n) use (&$fibonacci) 
{ 
    return ($n < 2) ? $n : $fibonacci($n - 1) + $fibonacci($n - 2); 
}); 

Zauważ, że global Definicja jest zastąpiona dzięki funkcji clousure i obsłudze pierwszej klasy PHP.

Inne rozwiązanie:

Można utworzyć class zawierające członków jako statycznych: fibonnacciMemo i $memo. Zauważ, że nie musisz już używać $memo jako zmiennej globalnej, więc nie spowoduje to żadnego konfliktu z innymi obszarami nazw. Oto przykład:

class Fib{ 
    //$memo and fibonacciMemo are static members 
    static $memo = array(); 
    static function fibonacciMemo($n) { 
     if(array_key_exists($n, static::$memo)) { 
     return static::$memo[$n]; 
     } 
     else { 
     if($n > 1) { 
      $result = static::fibonacciMemo($n-1) + static::fibonacciMemo($n-2); 
      static::$memo[$n] = $result; 
      return $result; 
     } 
     return $n; 
     } 
    } 
} 

//Using the same method by Edd Mann to benchmark 
//the results 

$start = microtime(true); 
Fib::fibonacciMemo(10); 
echo sprintf("%f\n", microtime(true) - $start); 
//outputs 0.000249 

$start = microtime(true); 
Fib::fibonacciMemo(10); 
echo sprintf("%f\n", microtime(true) - $start); 
//outputs 0.000016 (now with memoized fibonacci) 

//Cleaning $memo 
Fib::$memo = array(); 
$start = microtime(true); 
Fib::fibonacciMemo(10); 
echo sprintf("%f\n", microtime(true) - $start); 
//outputs 0.000203 (after 'cleaning' $memo) 

Używanie tego uniknąć użycia global a także problem czyszczenie cache. Althought, $memo nie jest zapisanie wątku i przechowywane klucze nie są wartości mieszające. Tak czy inaczej, można korzystać z wszystkich programów narzędziowych, takich jak php memoize memoize-php

+0

Tak, widziałem też ten wpis, jego bardzo pouczający i szczegółowy ... ale sposób w jaki wywołuje funkcję jest inny: '$ fibonacci (10)' ... czy to powoduje znaczną zmianę lub różnicę niż zwykła funkcja nazywa się "fibonacci (10)"? I wierzę, że to naprawdę nie jest memoization ale buforowanie w ogóle, a także obsługiwane przez jednego z komentarzy w poście jesteś wspólnego: również na wiki: Chociaż związane z buforowania, memoization odnosi się do konkretnego przypadku tego optymalizacja, odróżniając ją od form buforowania, takich jak buforowanie lub zastępowanie strony. – catzilla

+0

Nie ma różnicy między '$ fibonacci (10)' i 'fibonacci (10)', pierwszy jest obiektem, którego wartość jest funkcją anonimową (nie deklaracją funkcji taką jak druga). Widziałem ten komentarz na temat postu, a problem dotyczy pamięci: * nie ma sposobu na wyczyszczenie pamięci podręcznej * i * zużyto dużo pamięci *. Chociaż nie jest to ani [buforowanie] (http://en.wikipedia.org/wiki/Data_buffer) ani ten [zamiana stron] (http://en.wikipedia.org/wiki/Page_replacement_algorithm). –

+0

Dzięki za informacje, twoje drugie rozwiązanie działa dobrze dla mnie .. Jestem po prostu niewygodny przy użyciu metod klas statycznych dla problemu fibonacci .. Ale myślę, że używanie właściwości statycznej znacznie zmniejsza czas obliczania dla poprzednich wezwań do tego .. I dla pierwsze rozwiązanie, myślę, że spróbuję przetestować i zbadać na tym .. Myślę, że twoja odpowiedź zasługuje na głos .. Dzięki i tak .. :) – catzilla

2

myślę ... to powinien się do memoize się Fibonacciego:

function fib($n, &$computed = array(0,1)) { 
    if (!array_key_exists($n,$computed)) { 
     $computed[$n] = fib($n-1, $computed) + fib($n-2, $computed); 
    } 
    return $computed[$n]; 
} 

niektóre testy

$arr = array(0,1); 
$start = microtime(true); 
fib(10,$arr); 
echo sprintf("%f\n", microtime(true) - $start); 
//0.000068 

$start = microtime(true); 
fib(10,$arr); 
echo sprintf("%f\n", microtime(true) - $start); 
//0.000005 

//Cleaning $arr 
$arr = array(0,1); 
$start = microtime(true); 
fib(10,$arr); 
echo sprintf("%f\n", microtime(true) - $start); 
//0.000039 
+0

Cześć .. tak ta metoda działa też ... i ma również dobre punkty ... ale problem polega na tym, że musisz podać parametr '$ arr' do każdej deklaracji funkcji' fib() '.. jego najlepiej, że' $ arr' powinno być ukryte podczas używania funkcji 'fib() '.. – catzilla

+0

nie tak naprawdę, jeśli spojrzysz na sygnaturę funkcji, zadeklarowana tam tablica, jeśli nie przekażesz tablicy do funkcji, która zostanie ponownie zainicjalizowana w każdym wywołaniu, więc jest ukryta, ale jeśli planujesz użyj tej funkcji więcej niż raz, cóż, możesz zadeklarować swoją tablicę d go używać, możesz nawet, serializować go, przechowywać i przeładować później, używając tej samej funkcji bez modyfikacji ... –

+0

Cześć, próbowałem tej metody i tak, działało świetnie .. może źle zrozumiałem sposób '$ Parametr computed' jest używany, ale działał świetnie! do tej pory jest to najprostsza zapamiętana funkcja Fibonacciego, z którą się spotkałem! Spróbuję porównać tę funkcję z innymi implementacjami fibonacci i porównać wyniki ... dziękuję za odpowiedź! – catzilla