Złożoność obliczeniowa funkcji

0

Skąd mam wiedzieć jak upraszczać funkcje?
załóżmy f(x)=(2x+2)/2 to to samo co f(x)=x+1 a dziedzina funkcji jest ta sama. Tu na oko widać, że jest mniej obliczeń ale już
f(x)=(x+1)/2 to w tej postaci jest bardziej uproszczone czy w f(x)=x/2+0.5 ?
Czy to co ja piszę nie ma sensu bo kompilator i tak to zoptymalizuje? tak jak w przypadku kiedy dzielisz przez jakąś liczbę czy mnożysz przez odwrotność?
ale w przypadku tej funkcji u góry to nawet jak kompilator optymalizuje to jest to dłuższy zapis.
Pewnie jest sposób, żeby to zmierzyć ale nie wiem jaki.

4

To co proponujesz, to to nawet nie są mikrooptymalizacje, to są nanooptymalizacje… Pierwszej generacji Raspberry Pi wykonywało około 41 000 operacji zmiennoprzecinkowych na sekundę, oszczędzanie jednej czy dwóch nawet tam nie ma specjalnie sensu.

Ale jeśli z jakiegoś powodu chcesz się z tym babrać, to musisz sobie uświadomić wiele rzeczy na temat liczb zmiennoprzecinkowych. W szczególności dobrze jest zrozumieć, że f(x) = \frac{x+1}{2} i g(x) = \frac{x}{2} + 0{,}5 to nie są te same funkcje i kompilator nie może zastąpić jednej drugą¹.

Polecane lektury:

  1. https://floating-point-gui.de/
  2. https://gcc.gnu.org/wiki/FloatingPointMath

¹ Przy standardowych flagach wymuszających trzymanie się standardu.

4
  1. Nie używać obliczeń zmiennoprzecinkowych jeśli nie trzeba
  2. W zasadzie tyle, resztę zostaw kompilatorowi
0

Zastanawiałem się tak nad tym też bo w szkole na matmie jak coś liczymy to często można sobie życie upraszczać i robić mniej błędów.
Na arduino ma sens :).

0
Althorion napisał(a):

W szczególności dobrze jest zrozumieć, że f(x) = \frac{x+1}{2} i g(x) = \frac{x}{2} + 0{,}5 to nie są te same funkcje i kompilator nie może zastąpić jednej drugą¹.
(…)
¹ Przy standardowych flagach wymuszających trzymanie się standardu.

Czemu to nie są takie same funkcje, tak w jednym zdaniu?

0

Tak sobie patrzę na arduino i widzę że f(x)=x*0.3/0.3 jest bardziej złożone niż f(x)=x; ale to pewnie wina kompilatora. Ale wydajne dzielenie przez 2 też nie jest zaiplementowane

4
Silv napisał(a):
Althorion napisał(a):

W szczególności dobrze jest zrozumieć, że f(x) = \frac{x+1}{2} i g(x) = \frac{x}{2} + 0{,}5 to nie są te same funkcje i kompilator nie może zastąpić jednej drugą¹.
(…)
¹ Przy standardowych flagach wymuszających trzymanie się standardu.

Czemu to nie są takie same funkcje, tak w jednym zdaniu?

Zaokrąglenia. W matematyce to są te same funkcje, w informatyce - niekoniecznie

6
kamil kowalski napisał(a):

Tak sobie patrzę na arduino i widzę że f(x)=x*0.3/0.3 jest bardziej złożone niż f(x)=x; ale to pewnie wina kompilatora. Ale wydajne dzielenie przez 2 też nie jest zaiplementowane

Słowo Złożoność, rozumiana ściśle na gruncie matematyki / algorytmiki, to jest coś innego, niż dłubiecie w tym wątku. To określenie rzędu wielkości współczynnika, gdy ilość danych rośnie

To, co cię w powyższym zdaniu interesuje, wypada nazwać inaczej: czas wykonania (w (u/n)s, cyklach CPU) , ilośc kodu, stopien skomplikowania kodu.
Generalnie "nano-optymalizacja"

0
kamil kowalski napisał(a):

Tak sobie patrzę na arduino (...) Ale wydajne dzielenie przez 2 też nie jest zaiplementowane

Jest, nazywa się przesunięcie bitowe, dla intów działa.

Floaty są emulowane software’owo na tej platformie. Ale szczerze mówiąc jak program się wyrabia z obliczeniami i flasha masz dość, to też ich użycie nie jest aż takim problemem.

1
kamil kowalski napisał(a):

Zastanawiałem się tak nad tym też bo w szkole na matmie jak coś liczymy to często można sobie życie upraszczać i robić mniej błędów.

Na arduino ma sens :).

Jak chodzi o **masowe **obliczenia, to arduino w ogóle nie ma sensu jako baza projektu.
A jako obliczeń jest 1-5 na sekunde, to ten sztuczny wątek nie ma sensu, i tak arduino będzie napie...ć loop()

3

W szczególnych przypadkach na mikro-architekturach robi się mocne prace koncepcyjno -matematyczno -algorytmiczno -programowe, z których wynika uproszczony kod.
Prace są 100x "mądrzejsze" od klepania kodu: określenie dziedziny (zakresów) liczbowych, oczekiwanych dokładności (np wynik naszej w/w fuckji i tak będzie pompował 8 bitowy port, na co dokładność >40 bitowa), pełny rachunek błedów itd.

Analiza może się skończy stablicowaniem funkcji dla jakiegoś zakresu, sprytnym (smart) dodaniem dwóch czynników, chętnie potęg dwójki, zamianą na dwa zakresy liniowe z określeniem punktu załamania i wieloma innymi.

EDIT: na bardzo biednym procesorze Atmega 8 potrzebowałem 8 bitowy sinus w czterech ćwiartkach. Najpierw urżnąłem (co dość intuicyjne) trzy ćwiartki, potem dodalem 127, czyli miałem wyłącznie dodatnie liczby u8, stablicowałem.
A że projekt dotyczył dzwięku, i jakiekolwiek obliczenia dawały czkawkę, przetwornik C/A (dokładnie PWM) ustawiałem w pierwszym rozkazie przerwania, miałem zawsze tą samą ilość cykli, a potem przerwanie się zajmowało przeliczeniem wartości na następny raz.
Nawet było obliczenie/wyciszenie (metoda binarną) by koniec fali był w zerze, a nie dawał strzał do głośnika

Wiec jak trzeba, to się da. Tablicę sinusów oczywiście obliczył pecet i wydrukował plik C dla Atmela.

0

Tak sobie patrzę na arduino i widzę że f(x)=x*0.3/0.3 jest bardziej złożone niż f(x)=x; ale to pewnie wina kompilatora.

To, znowu, wina standardu liczb zmiennoprzecinkowych plus kolejności operatorów — * i / mają ten sam priorytet, więc są wykonywane od lewej do prawej, więc to wyrażenie to (x * 0.3) / 0.3, co wcale nie musi być tym samym co x. Nawiasem mówiąc, x / 1.0 to też nie jest to samo co x (bo ±0.0).

0

Jeśli chodzi o operacje na liczbach zmiennoprzecinkowych to to chyba zależy od komputera. Bo jak komputer wykonuje obliczenia w systemie 10 a nie 2 to nie ma problemu niedokładności(że danej liczby nie da się zapisać) pod warunkiem że używa się systemu dziesiętnego. Eniac działał w systemie 10 a nie 2 więc chyba nie miał problemu z dodaniem 0.1+0.2 i porównaniem 0.3. Ciekawe czy kiedyś w przyszłości też ponownie trzeba będzie na to zwracać uwagę.

1

Zapewniam cię, że stosowana podstawa systemu liczbowego nie ma wpływu na problemy jakie napotkasz przy zapisie liczb które mają nieskończone rozwinięcia przy danej podstawie.
Natomiast problem z liczbami zmiennoprzecinkowymi wynika z natury zapisu w postaci mantysy i eksponenty, np. wydawałoby się proste dodawanie składa się z kilku etapów: denormalizacja, włeściwe dodawanie, zaokrąglanie i ponowna normalizacja.

1 użytkowników online, w tym zalogowanych: 0, gości: 1