Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Potęgowanie rekurencyjne
lord2105
post
Post #1





Grupa: Zarejestrowani
Postów: 380
Pomógł: 59
Dołączył: 24.04.2010
Skąd: London

Ostrzeżenie: (0%)
-----


Witajcie mój problem brzmi następująco mam do napisania klase która w sposób rekurencyjny ma realizować potęgowanie i taką napisałem,

kolejną rzeczą jest to że obiekt tej klasy ma buforować wyniki i wykorzystać bufor zamiast wykonywania metody

co przez to rozumiecie?

Ten post edytował lord2105 17.08.2010, 17:07:35
Go to the top of the page
+Quote Post
flashdev
post
Post #2





Grupa: Zarejestrowani
Postów: 812
Pomógł: 117
Dołączył: 2.12.2008

Ostrzeżenie: (10%)
X----


To, że jeśli podnosisz np. N ^ 10, to można to rozpisać tak:

N ^ 10 = N ^ 8 * N ^ 2;
N ^ 8 = N ^ 4 * N ^ 4;
N ^ 4 = N ^ 2 * N ^ 2;
N ^ 2 = N ^ 1 * N ^ 1;
N ^ 1 = N;

Dzięki buforowaniu pośrednich operacji, zamiast wykonać 10 mnożeń, możesz wykonać ich tylko 4 w tym przypadku, ale przy większych liczbach korzyści są znacznie większe bo rosną funkcją potęgową.
Go to the top of the page
+Quote Post
everth
post
Post #3





Grupa: Zarejestrowani
Postów: 782
Pomógł: 153
Dołączył: 21.07.2010

Ostrzeżenie: (0%)
-----


Przekazuj w parametrze funkcji tablicę zawierająca wyniki poprzednich mnożeń - każde wywołanie dodaje nowy rezultat do tablicy - pod koniec powinieneś otrzymać tablicę z wynikami potęgi począwszy od 1 - zapamiętujesz ją we właściwości metody jako podtablicę z kluczem będącym twoją liczbą.

Przy kolejnych wywołaniach sprawdzasz czy dla danej liczby nie istnieje już tablica, jeśli tak to sprawdzasz czy zawiera wartość którą chcesz obliczyć - zwracasz ją. W przeciwnym wypadku bierzesz ostatnią wartość tablicy - powiedzmy w tablicy masz potęgę 3 liczby 2, ty szukasz potęgi 6 liczby 2 - zamiast stosować rekurencję od potęgi 1, stosujesz od 3 bo masz obliczoną jej wartość.

Pisane na szybko więc nie daję głowy że jest ok (przy moim pokrętnym rozumieniu matematyki zdziwiłbym się gdyby było).
Go to the top of the page
+Quote Post
lord2105
post
Post #4





Grupa: Zarejestrowani
Postów: 380
Pomógł: 59
Dołączył: 24.04.2010
Skąd: London

Ostrzeżenie: (0%)
-----


Po części chwyciłem o co Wam chodzi lecz bufor powinien występować bezpośrednio w klasie czy też przy wywoływaniu obiektu, pokaże co do tej pory napisałem w pliku class.calculate.php:

  1. <?php
  2.  
  3. //Class calculating power in a recursive
  4. class calculate_power
  5. {
  6. private $base;
  7. private $index;
  8.  
  9. public function getResult($base, $index)
  10. {
  11.  
  12. //declaration variables
  13. $this->base = $base;
  14. $this->index = $index;
  15.  
  16. if ($this->index == 0) {
  17. $result = 1;
  18. }
  19. else {
  20. $new_index = new calculate_power;
  21.  
  22. $result = $this->base * $new_index->getResult($this->base, $this->index-1);
  23. }
  24.  
  25. return $result;
  26. }
  27. }
  28. ?>

czy da sie zastosować bufor w tej klasie? Czy też jest on zbędny?

i show.php

  1. <?php
  2. require ('class.calculate.php');
  3.  
  4. $x = 3;
  5. $y = 3;
  6.  
  7. $calculator = new calculate_power;
  8.  
  9. echo $calculator->getResult($x,$y);
  10.  
  11. ?>


Ten post edytował lord2105 17.08.2010, 17:43:57
Go to the top of the page
+Quote Post
flashdev
post
Post #5





Grupa: Zarejestrowani
Postów: 812
Pomógł: 117
Dołączył: 2.12.2008

Ostrzeżenie: (10%)
X----


Cytat(lord2105 @ 17.08.2010, 18:41:32 ) *
[...] lecz bufor powinien występować bezpośrednio w klasie czy też przy wywoływaniu obiektu [...]


Lepiej bezpośrednio w klasie, ponieważ wtedy, przy kolejnych obliczeniach, klasa już będzie miała wykonaną część obliczeń.
Go to the top of the page
+Quote Post
everth
post
Post #6





Grupa: Zarejestrowani
Postów: 782
Pomógł: 153
Dołączył: 21.07.2010

Ostrzeżenie: (0%)
-----


@lord105 - nie wywołuj rekurencyjnie w metodzie tworzenia nowego obiektu. To kompletnie bez sensu. Wywołuj samą metodę tego samego obiektu np.
  1. public function getResult($x,$y) {
  2. $this->getResult($x,$y-1); //czy jak tam to zaimplementujesz
  3. }

@flashdev - na upartego to można cache zadeklarować jako właściwość statyczną - wtedy wszystkie obiekty będą operowały na jednym. Unikamy dzięki temu zbędnej redundancji.
Go to the top of the page
+Quote Post
thek
post
Post #7





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Flashdev: Twoja metoda jest dobra dla dużych potęg, jednak dla niższych niekoniecznie. Zwróć uwagę na konieczność sprawdzania czy dana potęga już aby nie wystąpiła w trakcie obliczeń. Jak chcesz rozwiązać ten akurat problem? Deklarowanie tablicy wyników potęg typu przykładowo dla 5 array( 1 => 5, 2 => 25, 4 => 625, 8 => 390625) a potem sprawdzając czy mnożenie potęgi aktualnej * 2 jest większe od wymaganej? Jeśli tak wykonaj operację, dodaj do tablicy potęg i rób dalej rekurencję. Jeśli nie leć dodaj bezpośrednio niższą potęgę i sprawdź czy większa od wymaganej. Jeśli nie dodaj potęgę a wynik pomnóż przez odpowiadającą mu wartość. I tak aż do osiągnięcia właściwej potęgi. Trochę zagmatwanie napisałem, ale chyba rozumiesz o co chodzi.

Jednym zdaniem: "Dla względnie małych potęg naddatek operacji sprawdzających odbije się negatywnie na wydajności i ta rekurencja puszczona wprost na koprocesor wykona się znacznie szybciej, bo ma on dedykowane rejestry do tego typu zadań."
Go to the top of the page
+Quote Post
flashdev
post
Post #8





Grupa: Zarejestrowani
Postów: 812
Pomógł: 117
Dołączył: 2.12.2008

Ostrzeżenie: (10%)
X----


Cytat(thek @ 18.08.2010, 09:26:07 ) *


Specjalnie nie podałem gotowego algorytmu, żeby zmusić czytelnika do myślenia. Ale widzę, że nie znając jeszcze tego prostego algorytmu już udało Ci się wykonać jego analizę wydajnościową (IMG:style_emoticons/default/smile.gif)

Poniżej przedstawiam mój (no bez przesady, jaki tam mój... ja nie wymyślam na poczekaniu takich mądrych rzeczy) algorytm.

Kod
// zadanie: a ^ b

// Przeliczamy b na system binarny. A tak naprawdę to nie przeliczamy, bo ona już jest zapisana binarnie. Użyjemy funkcji: bin(numer_bitu, liczba_int);

Liczymy długość pętli:
N = ceil(log2(b));

res = 1;
mul = a;
for( i = 0; i < N; i++ ){
res *= bit(i, b) ? mul : 1;
mul *= mul;
}


To cały algorytm - jedna pętla. Nie jest to co prawda rekursja, ale jak wiadomo rekursję można zrealizować iteracyjnie i vice versa.

Tak przy okazji to dokładnie w ten sam sposób działa algorytm szybkiego mnożenia, tylko że tam zamiast *=, będzie +=, oraz niezmiennik mnożenia (1), będzie zastąpiony niezmiennikiem dodawania (0).

Ten post edytował flashdev 18.08.2010, 10:25:11
Go to the top of the page
+Quote Post
thek
post
Post #9





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Swój algorytm oparłem o logiczne przemyślenie kroków, bez optymalizacji w stylu "przechodzimy na binarkę", czyli cały czas trzymając się systemu dziesiętnego (IMG:style_emoticons/default/smile.gif) Po przeskoku zyskamy, ale nadal dla odpowiednio niskich potęg różnica będzie albo niezauważalna, albo bardzo mała by konieczne było wprowadzenie szybkiego potęgowania. Jeśli zaś już mielibyśmy to optymalizować to, przynajmniej w C, zmiana z i++ na ++i też przyniesie wzrost szybkości. A że kod mi wygląda podobnie do C to użycie czegoś w stylu register dla i ( o ile język ten to umożliwia) jeszcze bardziej by zwiększyło wydajność.

Ogólnie jednak rzecz ujmując na pomysł z ifem na bitowej reprezentacji potęgi nie wpadłem, a faktycznie przyspiesza całość bo rozwiązuje problem "potem sprawdzając czy mnożenie potęgi aktualnej * 2 jest większe od wymaganej" w sposób nie wymagający dużej ilości sprawdzeń. Raptem jedno by sprawdzić czy binarna wersja ma ustawione 1 dla tejże pozycji (IMG:style_emoticons/default/smile.gif) Pomysł prosty i wydajny jednocześnie. Człowiek uczy się jednak całe życie. Dzięki flashdev za wskazówkę bo na bank się kiedyś mi przyda, zważywszy, że co jakiś czas bawię się binarnie z liczbami.
Go to the top of the page
+Quote Post
flashdev
post
Post #10





Grupa: Zarejestrowani
Postów: 812
Pomógł: 117
Dołączył: 2.12.2008

Ostrzeżenie: (10%)
X----


No skoro ta różnica będzie niezauważalna, albo nawet bardzo mała, to po co komplikować sobie życie i wprowadzać jakieś if`y? (IMG:style_emoticons/default/smile.gif)

A co do tego algorytmu, to gwarantuję Ci, że gdziekolwiek nie spojrzysz na jakieś sprzętowe implementacje potęgowania, lub gotowe wbudowane funkcje w języki programowania, to właśnie będzie to tak zrobione.

A i należy jeszcze pamiętać, że tutaj wspomniany przez Ciebie koprocesor będzie miał wątpliwe zastosowanie, bo już przy skromnym wykładniku potęgi > 32 i postawie potęgi równej 2 wychodzimy poza zakres int, więc jest konieczne użycie własnego typu danych.
Go to the top of the page
+Quote Post
lord2105
post
Post #11





Grupa: Zarejestrowani
Postów: 380
Pomógł: 59
Dołączył: 24.04.2010
Skąd: London

Ostrzeżenie: (0%)
-----


Dziękuję bardzo za wyczerpujące odpowiedzi ale czy może ktoś powiedzieć w którym Dokłanie miejscu bufor powinien zbierać informacje a w którym sprawdzać czy dany wynik już wystąpił?
Go to the top of the page
+Quote Post
flashdev
post
Post #12





Grupa: Zarejestrowani
Postów: 812
Pomógł: 117
Dołączył: 2.12.2008

Ostrzeżenie: (10%)
X----


Ponieważ nie wytłumaczyłem (uznałem to za oczywiste) co robi moja funkcja bin, a autor tematu dopytuje się o nią na PW zamieszczam poniżej co miałem na myśli.

  1. // funkcja zwraca kolejny ($num_bit) bit liczby $n
  2. function bin($num_bit, $n){
  3. return (( 1 << $num_bit ) & $n ) ? 1 : 0;
  4. }
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 3.04.2026 - 18:25