Ostatnia wartość zwracana przez funkcję

2010-01-19 9:23

Debugując program w pracy krokowej w Visual Studio, możemy podglądać wartości wszystkich zmiennych, jakie są dostępne w zasięgu punktu wykonania. Jest to możliwe za pośrednictwem okienka tzw. czujek (watches). Automatycznie wypełnia się ono zmiennymi lokalnymi oraz ewentualnym wskaźnikiem this, pozwalającym podejrzeć wartości pól obiektu, jeśli znajdujemy się akurat w jego metodzie.
Jednak oprócz rzeczywistych zmiennych możemy śledzić też pewne specjalne „pseudozmienne”, udostępniane przez debuger VS. Mają one nazwy zaczynające się od znaku dolara $ i potrafią być bardzo przydatne, co pokażę na przykładzie.

Powiedzmy, że mamy kod składający się z wielu następujących po sobie wywołań funkcji bibliotecznych. Każda z nich zwraca rezultat liczbowy, informujący o (nie)powodzeniu operacji, którego jednak w kodzie nie sprawdzamy – pewnie dlatego, że nie chciało nam się pisać tych wszystkich ifów :) Taka sytuacja może wystąpić chociażby przy ciągu wywołaniach DirectX: ogromna większość funkcji z tej biblioteki zwraca wynik typu HRESULT, który jest liczba zawierającą kod błędu, jeśli takowy wystąpił.
Zauważamy teraz, że ów ciąg wywołań najpewniej zawiera jakiś błąd. Objawem w DX może być chociażby słynne „nic nie widać” :) Jak teraz dojść, które z wielu wywołań zwraca błąd, jeśli w żadnym z nich nie tylko nie sprawdzamy wyniku, ale wręcz w ogóle go nie zapisujemy?…

Podejrzenie wartości EAX w VS

Istnieje na szczęście proste i wygodne rozwiązanie, które nie wymaga żadnych zmian w kodzie. Otóż przy pomocy debugera VS i jego pseudozmiennych możemy podejrzeć wartość rejestrów procesora. Wystarczy użyć symbolu $nazwa_rejestru, jak np. $eax. I właśnie $eax jest tutaj świetnym przykładem, bo rozwiązuje nasz problem. Otóż przez rejestr EAX przekazywany jest zawsze 32-bitowy rezultat zwracany przez każdą porządną funkcję. Przechodząc więc krokowo po naszym kodzie możemy sprawdzić, jak zmienia się ten rejestr po każdym wywołaniu, podglądając w ten sposób wartość, jaką zwróciła w nim funkcja. Wystarczy więc tylko znaleźć to, w którym rezultat informuje o niepowodzeniu… i voila – mamy nasz błąd :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Indeksowanie od tyłu

2010-01-12 20:58

Pewna programistyczna mądrość ludowa uczy, że jedynymi liczbami bezpośrednio występującymi w kodzie powinny być tylko 0 lub 1. Brak lub nadmiar tej drugiej jest przy tym często pojawiającym się błędem, który zyskał swoją własną nazwę pomyłek o jedynkę (off-by-one errors).

Okazji do popełnienia tego rodzaju gaf jest sporo, a objawiają się one przede wszystkim wtedy, gdy mamy do czynienia z tablicami, łańcuchami znaków, kolekcjami, itd. - innymi słowy wszystkim, co da się indeksować. Jako rzecze C++ i większość innych "normalnych" języków, indeksy tablic rozpoczynają się od zera. Startowanie ich od jedynki ma aczkolwiek pewną zaletę: pętle przeglądające zawartość tablic mają wtedy prawie identyczne postaci niezależnie od kierunku przeglądania:

for (int i = 1; i <= n; ++i) // w przód
for (int i = n; i>= 1; --i) // w tył

W przypadku indeksowania od zera pętla odliczająca wstecz jest wyraźnie różna:

for (int i = 0; i <n; ++i) // w przód
for (int i = n - 1; i>= 0; --i) // w tył

Pisząc ją, trzeba więc zwrócić baczniejszą uwagę na to, co się robi.

Jednak nawet wtedy można potknąć się o pewien kruczek, gdy pod n podstawimy rzeczywiście używane wartości. Niech to będzie np. wielkość kolekcji STL czy długość łańcucha znaków std::string - a więc coś w stylu x.size(). Otóż takie wyrażenie zwraca liczbę typu równoważnego size_t, który z kolei jest równy ni mniej, ni więcej, jak tylko unsigned int. Jest to więc liczba bez znaku.
W zwykłej wersji pętli (liczonej do przodu) powoduje to ostrzeżenie kompilatora o niezgodności typów ze znakiem i bez znaku (signed/unsigned mismatch) w warunku i < x.size(), gdy i jest typu int. Jednym ze sposobów na pozbycie się tego warninga jest oczywiście zamiana typu licznika na size_t. Jeśli teraz mamy wystarczająco zły dzień, to przez analogię będziemy licznikiem tego typu indeksować również pętlę odwrotną:

for (size_t i = x.size() - 1; i>= 0; --i) // no i zonk

I nieszczęście gotowe. Zauważmy bowiem, że odliczanie do tyłu tablicy/kolekcji indeksowanej od zera wymaga, by na koniec licznik przyjął na koniec wartość ujemną; inaczej warunek i >= 0 nigdy nie stanie się fałszywy. To się jednak nigdy nie stanie, gdy i jest liczbą bez znaku; zamiast tego nastąpi przekręcenie na maksymalną wartość dodatnią. Skutkiem będzie pętla nieskończona lub (częściej) access violation.

Co z tego wynika? Ano to, żeby... indeksowania generalnie unikać :) Po to bowiem zarówno STL, jak i każda inna biblioteka pojemników w każdym sensownym języku ma inne sposoby dostępu do elementów - choćby iteratory - aby z nich korzystać. I unikać takich "ciekawych" błędów jak powyższy :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: , , ,
Autor: Xion w Programowanie » Komentarze (6)

Siedem. Windows 7

2010-01-09 13:27

To, że przez kilka dni nie skrobnąłem żadnej nowej notki to prosta konsekwencja zasady, że skoro nie ma o czym pisać, to można nie pisać w ogóle. Ponieważ jednak stosowanie tej reguły na dłuższą metę będzie dla bloga fatalne, postanowiłem ją niniejszym zarzucić ;-)
"Logo" Windows 7No, a wyrażając się już nieco poważniej... Minęło już trochę czasu, od kiedy zainstalowałem Windows 7, więc mogę się już podzielić nie tylko pierwszymi wrażeniami z codziennego użytkowania tego systemu. W takich przypadkach zresztą te późniejsze wrażenia są ponadto znacznie ważniejsze.

Jakoś tak bowiem wychodzi, że wraz z upływem czasu każda instalacja systemu operacyjnego zaczyna tracić na wydajności. To takie informatyczne zastosowanie drugiej zasady termodynamiki. I chociaż entropię (czyli bałagan) na dysku da się jednak zmniejszyć - chociażby poprzez defragmentację - to ilość danych, z jaką musi się zmierzyć system, regularnie rośnie. Produkuje je bowiem każdy sposób korzystania z komputera i każde uruchomienie dowolnego programu.
Jak więc z okiełznaniem tego powiększającego się nieporządku radzi sobie Windows 7? Otóż nie najgorzej. Poza nieznacznym wydłużeniem czasu ładowania samego systemu, po ponad dwóch miesiącach jego użytkowania nie zauważam znaczących spadków wydajności w działaniu. Pomaga przy tym pewnie fakt, że Windows 7 koncentruje się głównie na optymalizowaniu często wykonywanych czynności, jak chociażby uruchamiania się programów na stałe "przypiętych" do paska zadań. Trudno to jednak uznać za coś złego.
Całkiem dobrze funkcjonuje też inny element wpływający na postrzeganie wydajności systemu, czyli scheduler, tj. moduł szeregujący procesy i wątki. Na tyle dobrze rozpoznaje on aplikacje, z którymi aktualnie pracuje użytkownik, że przełączanie między nimi nie sprawia problemów. Dotyczy to nawet przechodzenia między Pulpitem a pełnoekranowymi grami, w trakcie którego czasu totalnego "zaciemnienia" powodowanego zmianą rozdzielczości jest naprawdę minimalny.

Co poza tym? Właściwie nic specjalnego. Tak naprawdę Windows 7 jest tym, czym wcześniej powinna być Vista - systemem, gdzie cukierkowe GUI nie służy tylko przykryciu niedorobionego wnętrza. Jest tu oczywiście kilka miłych, przydatnych i cieszących oko drobiazgów, jak choćby nowy pasek zadań, o którym zdążyłem już wspomnieć. Jest również kilka feature'ów mniej użytecznych - albo przynajmniej takich, których użyteczności nie udało mi się w porę zauważyć - jak choćby funkcja Aero Peek, dzięki której można "na chwilę" podejrzeć Pulpit (a raczej jego zapamiętany obraz). Da się je na szczęście wyłączyć :)

Czy warto więc w nową wersję Windows się zaopatrzyć? Jeśli dotąd wystarczała nam edycja XP i nie potrzebujemy chociażby wsparcia dla DX10+, to sprawa wymaga głębszego przemyślenia. Z kolei dla osób, którego z tego lub innych względów były dotąd "skazane" na Vistę, wybór Windows 7 będzie długo oczekiwanym upgrade'em :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: ,
Autor: Xion w Informatyka » Komentarze (8)

Triki z PowerShellem #13 – Prędkość czasu

2009-12-31 13:47

Ostatni dzień roku to dobry moment na różne dziwne przemyślenia na temat czasu. Kiedyś z tej okazji wymyśliłem zupełnie nowy system kalendarza, a dzisiaj przypomniałem sobie o niezupełnie poważnym pomyśle autorstwa Rega, ze zbliżonej dziedziny.
Chodzi o oryginalną wielkość "fizyczną": prędkość czasu. Ma ona ilościowo mierzyć znany wszystkim fakt, że wrażenie upływającego czasu jest różne w różnych sytuacjach. Gdy w rzeczywistości upłynął pewien czas t, a nam wydawało się, że minął raczej czas t', to odczuwana przez nas prędkość czasu \tau wynosi

\displaystyle \tau = \frac{t}{t'}.

Dla przykładu: na nudnym wykładzie mogło minąć ledwie 15 minut, podczas gdy nam wydaje się, że siedzimy na nim już godzinę; wówczas \tau_1 = 0,25 / 1 = 0,25. Z kolei grając przez trzy godziny możemy mieć wrażenie, że upłynęło tylko 30 minut - wtedy \tau_2 = 3 / 0,5 = 6. Jak widać \tau_1 <\tau_2, zatem teoria zgadza się tutaj z intuicją, co pozwala wnioskować, że wielkość jest dobrze zdefiniowana :)

Pozostaje jeszcze kwestia jej pomiaru. W swoim oryginalnym artykule (który w tajemniczy sposób zniknął z sieci) Reg zaproponował, że można by go przeprowadzić za pomocą odpowiedniego programu. Miałby on działać w tle, losować pewien interwał czasu i po jego upływie pytać użytkownika, ile czasu według niego upłynęło od ostatniego pytania. Z dokładnością do małych błędów pomiarowych (np. czasu potrzebnego na wpisanie odpowiedzi) metoda wydaje się dobra - czemu więc by jej nie zaimplementować? :]
To właśnie uczyniłem, posługując się oczywiście swoim ulubionym narzędziem na takie okazje, czyli PowerShellem. Pewnym problemem było to, jak w skrypcie konsolowym postarać się o mierzenie czasu w sposób, który byłby niezauważalny dla użytkownika. Da się to jednak łatwo zrobić, posługując się... wbudowanym w system Harmonogramem zadań (Task Scheduler). Istnieje bowiem do niego dobrze udokumentowane, ładne i przejrzyste COM-owe API (dostępne od Visty wzwyż), którego użycie w PowerShellu wygląda zresztą kilka razy lepiej niż w C++ ;P
Wynikowy skrypt wygląda więc następująco:

# timespeed.ps1
# Skrypt do mierzenia prędkości czasu
param ([int]$minutes = 0)

# Stałe
$TASK_NAME = "TimeSpeed_Task"   # Nazwa zadania w Harmonogramie

# Pobieramy obiekt harmonogramu zadań
$ts = New-Object -ComObject Schedule.Service
$ts.Connect($null, $null, $null, $null)
$tf = $ts.GetFolder($(New-Object String @([char]92, 1))) # "\"

if ($minutes -eq 0)
{
    # Pierwsze uruchomienie
    $task = $ts.NewTask(0)
    $task.Data = ""
    $continue = "T"
}
else
{
    # Kolejne uruchomienia
    $task = $tf.GetTask($TASK_NAME).Definition
   
    # Pytamy użytkownika i liczymy prędkość czasu
    $userMinutes = Read-Host -Prompt "Liczba minut od ostatniego razu"
    $d_tau = $minutes / $userMinutes
    if (-not [String]::IsNullOrEmpty($task.Data))
    {
        $timeData = $task.Data.Split(',')
        $totalTime = [int]$timeData[0]
        $totalUserTime = [int]$timeData[1]
    }
    $totalTime += $minutes
    $totalUserTime += $userMinutes
    $tau = $totalTime / $totalUserTime
    $task.Data = $totalTime.ToString() + "," + $totalUserTime.ToString()
   
    # Wyświetlamy aktualne wyniki
    "Rzeczywista liczba minut: " + $minutes | Out-Host
    "Chwilowa prędkość czasu: " + $d_tau | Out-Host
    "Całkowita prędkość czasu: " + $tau | Out-Host
   
    # Pytamy użytkownika, czy chce kontynuować
    $continue = Read-Host -Prompt "Wykonać następny pomiar (T/n)?"
}

if ($continue -ine "n")
{
    # Losujemy nowy interwał czasowy (od 5 do 60 minut, co 5)
    $rand = New-Object System.Random
    $nextMinutes = ($rand.Next(0, 12) + 1) * 5
   
    # Dodajemy trigger dla zadania (czas: za X minut)
    $task.Triggers.Clear()
    $trigger = $task.Triggers.Create(1) # 1 - trigger na określony czas
    $nextTime = [DateTime]::Now.AddMinutes($nextMinutes)
    $trigger.StartBoundary = $nextTime.ToString("o")
   
    # Dodajemy akcję (uruchomienie skryptu)
    $task.Actions.Clear()
    $action = $task.Actions.Create(0)   # 0 - uruchomienie polecenia
    $action.Path = "powershell"
    $action.Arguments = "-Command . '" + $MyInvocation.MyCommand.Path
    $action.Arguments += "' -minutes " + $nextMinutes.ToString()
   
    # Dodajemy zadanie
    # (6 - stwórz lub uaktualnij)
    # (3 - zadanie uruchamiane tylko przy zalogowanym użytkowniku)
    $tf.RegisterTaskDefinition($TASK_NAME, $task, 6, $null, $null, 3) | Out-Null
}
else
{
    # Koniec - usuwamy zdarzenie
    try { $tf.DeleteTask($TASK_NAME, 0) } catch { }
}

Przy swoim pierwszym uruchomieniu tworzy on nowe zadanie w Harmonogramie, które polega na jego ponownym odpaleniu po upływie losowej liczby minut (5, 10, 15, itd. aż do maks. 60 minut). Wtedy użytkownik jest pytany o to, ile czasu według niego upłynęło, a następnie wyświetlają się częściowe wyniki pomiaru. Wówczas można zdecydować o jego kontynuowaniu lub zakończeniu. (Zawsze można też ręcznie usunąć zadanie z Harmonogramu, rzecz jasna).

Skrypt w PSh do mierzenia prędkości czasu

I to w sumie tyle. Można już przystąpić do badań określających dokładnie, jak bardzo dłuży nam się czas do północy :))

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Odczytywanie tekstu z gwiazdek

2009-12-29 12:24

Pole tekstowe hasłaAplikacje wymagające podania od użytkownika jakichś haseł zwykle ukrywają ich znaki, zastępując je "gwiazdkami", czyli np. znakami asterisk (*) lub dużymi kropkami. Dzięki temu lekko zwiększa się poziom bezpieczeństwa programu, gdyż osoba niepowołana nie może tak po prostu odczytać hasła, patrząc na ekran.
Niezbyt imponujące określenie 'lekko' jest tu jednak bardzo na miejscu, bo istnieje całe mnóstwo narzędzi, które potrafią takie "zagwiazdkowane" hasła bez trudu odczytać. W jaki sposób to robią?

Otóż pola tekstowe dla haseł często są po prostu zwykłymi editboksami. Różnicą jest to, że mają przy tym ustawioną specjalną flagę: styl ES_PASSWORD; sprawia ona, że zawartość pola jest wyświetlana w sposób zamaskowany. Zakładając, że znamy jego uchwyt (HWND) - pozyskany na przykład z pozycji myszy i funkcji WindowFromPoint - możemy ów styl po prostu wyłączyć:

SetWindowLong (hEditBox, GWL_STYLE,
    GetWindowLong(hEditBox, GWL_STYLE) & ~ES_PASSWORD);

a następnie ustawić jeszcze znak zastępczy ("gwiazdkę") na \0:

SendMessage (hEditBox, EM_SETPASSWORDCHAR, 0, 0);

Wtedy kontrolka powinna zacząć normalnie wyświetlać swoją zawartość bez zamaskowania, więc użytkownik będzie w stanie ją odczytać. Jeśli natomiast chcielibyśmy programowo się do niej dostać, trzeba wysłać do kontrolki komunikat WM_GETTEXT. (Funkcja GetWindowText nie zadziała, jeśli editbox jest w innym procesie - a w tym przypadku właśnie tak jest).

Czy można swój własny program zabezpieczyć przed takim "hackiem"? Oczywiście można - wystarczy nadpisać domyślny sposób obsługi wspomnianego wyżej komunikatu WM_GETTEXT, by nie zwracał on rzeczywistego tekstu zapisanego w polu tekstowym. Da się to zrobić przy pomocy techniki subclassingu, czyli podmiany procedury zdarzeniowej okna (tutaj: pola tekstowego). Trzeba tylko pamiętać, żeby zapewnić przy okazji jakiś sposób, żeby ten tekst jednak jakoś pobierać - np. poprzez zupełnie nową wiadomość:

const DWORD WM_GETREALTEXT = WM_USER + 0x0177;

Najpewniejszą metodą jest aczkolwiek zupełne zrezygonowanie z przechowywania sensownej zawartości w samym editboksie i użycie do tego jakiegoś zewnętrznego bufora w postaci zmiennej. To jednak wymaga obsługi komunikatów związanych z całym procesem edycji, aby ten zewnętrzny bufor uaktualniać.

Istnieją oczywiście gotowe implementacje kontrolek, które działają właśnie w ten sposób. Czy warto ich używać? Sądzę, że niekoniecznie - z dwóch powodów.
Po pierwsze, pole tekstowe do wprowadzania hasła nie musi być wcale najwęższym gardłem bezpieczeństwa aplikacji. Niewiele przyjdzie z nawet najlepszego zabezpieczenia go, jeśli potem wpisane doń hasło jest np. przesyłane otwartym tekstem przez sieć lub zapisywane w pliku w postaci hasha MD5.
Po drugie, użytkownicy są generalnie przyzwyczajeni do jednokrotnego wpisywania haseł, które są następnie zapisywane między sesjami. Tak się dzieje chociażby na stronach WWW, gdzie w tym celu korzysta się z ciasteczek (cookies). Hasła więc bywają zapominane, lecz w sieci nie jest to wielkim problemem ze względu na powszechne mechanizmy przypominania. Tradycyjne aplikacje ich nie posiadają, więc istnienie jakieś metody pozwalającej użytkownikowi na odzyskanie z nich haseł (czyli użycie "odgwiazdkowujących" programów) nie jest wcale rzeczą złą.

A że przy okazji może to zrobić ktoś niepowołany? No cóż... Jeśli posiada on już dostęp do komputera ofiary oraz prawa administratora w jej systemie (bo to jest potrzebne, by wymienione wyżej triki działały), to jest wątpliwe, żeby poprzestał tylko na zabawie z gwiazdkami w editboksach ;-)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

10 lat w sieci

2009-12-25 20:46

Tegoroczny koniec grudnia to czas podsumowań tego, co działo się nie tylko w ciągu ostatnich 12 miesięcy, ale i całej ostatniej dekady. A przypadkiem właśnie dekadę temu zyskałem dostęp do globalnej sieci zwanej Internetem... Jest to więc ciekawa okazja na podzielenie się paroma moimi obserwacjami tego, jak na przestrzeni ostatnich 10 lat Internet się zmieniał.

Rok 1999 to nie były jakieś zamierzchłe czasy sieci w Polsce (nie wspominając już o sieci w ogóle), lecz mimo to podejrzewam, że niezbyt duża część obecnych jej użytkowników dobrze je pamięta. Cechą wyróżniającą Internetu w tamtym okresie była przede wszystkim oszczędność. Szerokopasmowe łącza nikomu się wtedy nie śniło, a część internautów musiała płacić za sam czas spędzony w sieci (sławetny numer 0202122). Dlatego też podstawową zasadą netykiety było możliwie najskuteczniejsze zmniejszanie rozmiarów przesyłanych danych. Objawiało się to dość spartańskim wyglądem ówczesnych stron, z rzadka składającym się z większej ilości obrazków, a często wykorzystującym takie niefortunne wynalazki HTML jak choćby ramki.
Jak nietrudno zauważyć dzisiaj jest zupełnie inaczej. Główna strona typowego portalu może spokojnie ważyć kilka megabajtów i być okraszona kilkoma reklamowymi animacjami typu Flash, często zresztą niczym nie różniących się od spotów telewizyjnych. Wymaga ona więc znacznie większej przepustowości łącza, a także o wiele lepszej i efektywniejszej przeglądarki.

To też zresztą znak rozpoznawczy Internetu końca dekady: jest on niemal tożsamy z WWW, a inne protokoły komunikacji są w głębokim odwrocie. Kilka lat wcześniej było pod tym względem inaczej. Przeglądanie stron było tylko jedną z wielu sieciowych aktywności, do których należało też korzystanie z e-maila, ściąganie plików przez FTP, czytanie grup dyskusyjnych (Usenet) czy pogaduszki za pośrednictwem IRC. Dzisiaj prawie każdy z tych kanałów komunikacji został pochłonięty lub zastąpiony przez tzw. aplikacje webowe, czyli po prostu trochę bardziej interaktywne wersje stron WWW.

I wreszcie trzeci aspekt zmian, jakie w sieci się dokonały: jej umasowienie. Nie chodzi tu tylko o sam wzrost liczby osób korzystających z sieci, który w ciągu ostatnich 10 lat był prawie czterokrotny. Chodzi o pewne zmiany w samej "strukturze" sieci, które są przynajmniej częściowo jego wynikiem.
Kiedyś w Przekroju czytałem artykuł reklamowany na okładce wiele mówiącą frazą: "Jak idioci zepsuli nam Internet". Dotyczy ona oczywiście całego sieciowego trendu znanego pod ogólnym określeniem Web 2.0, nad którym zresztą pastwiłem się już przy innej okazji. W skrócie chodzi mniej więcej o to, że połączenie masowości dostępu do Internetu (w krajach rozwiniętych to już przynajmniej połowa populacji) z łatwością umieszczania w nim nowych treści (coraz mniejsza konieczność znajomości takich "technikaliów" jak choćby HTML) powoduje zalanie sieci contentem bez żadnej wartości, czyli jej zwyczajne zaśmiecenie.
W sumie jednak jest to nic nowego; już przed 10 laty mówiło się, że zawartością Internetu są w większości bzdury. Różnica między stanem z tego czasu a dniem dzisiejszym jest jednak taka, że mimo bezwzględnego (a pewnie i względnego) wzrostu ilości treściowych odpadów znalezienie poszukiwanych informacji jest paradoksalnie łatwiejsze niż kiedykolwiek wcześniej. Zawdzięczamy to jednak tylko i wyłącznie gwałtownemu rozwojowi internetowych wyszukiwarek.

Ogólny bilans tej dekady w sieci nie jest więc jednoznaczny. Z jednej strony stoją rzecz jasna MySpace, Facebooka, 4chana czy inne sieciowe szkodniki, ale z drugiej jest przecież Google, Wikipedia i całe mnóstwo wartościowych serwisów tematycznych, z których część nie powstałaby pewnie w warunkach "elitarnego" Internetu sprzed dekady.
Jesteśmy więc w innym miejscu - lecz niekoniecznie gorszym. Trzeba się do niego po prostu przyzwyczaić :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Niekończące się liczby

2009-12-22 14:18

Symbol nieskończonościNieskończoność jest jednym z tych pojęć matematycznych, które z samej natury nie dają się odnieść do codziennego życia. W informatyce chyba najbliższym do niej odniesieniem jest nieskończona pętla - czyli taka, której warunek stopu nigdy nie będzie prawdziwy. Wiadomo jednak, że w rzeczywistości ową pętlę prędzej czy później przerwie jeśli nie debugujący kod programista, to jakiekolwiek inne zdarzenie z gatunku losowych, z brakiem prądu włącznie.
Dlatego też intuicyjne pojmowanie tego pojęcia często bywa nieadekwatne do jego faktycznego, matematycznego znaczenia. Można się było o tym przekonać niedawno za sprawą pewnego wątku na forum Warsztatu.

0,(9), czyli 1Dotyczył on pewnego matematycznego faktu - iż liczba 0,(9) równa się po prostu 1. Zapis 0,(9) oznacza tutaj - zgodnie z przyjętą powszechnie konwencją - ciąg 0,999... nieskończonej liczby dziewiątek w rozwinięciu dziesiętnym liczby.
Powyższa równość bywa często kwestionowana przez osoby niezbyt dobrze zaznajomione z bardziej abstrakcyjnymi koncepcjami matematycznymi, jak chociażby granice, szeregi czy własności zbioru liczb rzeczywistych. "Intuicyjnie" - aczkolwiek zupełnie błędnie - może się wydawać, że 0,(9) nie jest równe jeden, a jest jedynie liczbą bardzo, bardzo jej bliską. Najlepiej tak bliską, że już żadnej bliższej być nie może.

To oczywiście jest kompletną bzdurą, bo taka liczba nie istnieje. Jedną z najważniejszych cech liczb rzeczywistych jest właśnie to, że:

\displaystyle \forall a,b \in \mathbb{R}, a <b \  \exists x \in \mathbb{R} \quad a<x <b

czyli że dla dowolnych dwóch takich liczb zawsze da się znaleźć trzecią, dającą się "włożyć" między nimi. Co więcej, takich pośrednich wartości jest całkiem sporo, bo dokładnie tyle samo co w całym zbiorze \mathbb{R} - nieskończenie wiele.
I tu właśnie już wyraźnie pojawia się nieskończoność, która - jak sądzę - jest głównym "winowajcą" stwierdzeń, że jakoby 0,(9) \ne 1. Gdy spotykamy się z nim, dość naturalną reakcją jest bowiem zapytanie, ile wobec tego wynosi różnica |1 - 0,(9)|. Następująca dalej próba zapisania rzekomej "liczby tak bliskiej zera, że już bliższej nie ma" kończy się następnie wyprodukowaniem potworka w rodzaju 0,(0)1. W założeniu ma być on odczytywany jako liczba, która w zapisie dziesiętnym ma nieskończoną liczbę zer, po których następuje jedna jedynka...

Chwila! Jak w ogóle coś może występować na końcu nieskończonego ciągu?!... Ano właśnie - tutaj następuje efektowna logiczna sprzeczność, która ładnie dowodzi nieprawidłowości założenia (czyli nieszczęsnej równości 0,(9) \ne 1). Pokazuje też, że mamy tu w gruncie rzeczy z nierozumieniem pojęcia tzw. nieskończoności potencjalnej, czyli najprostszego ujęcia idei nieskończoności: jako takiej ilości, która jest większa od każdej, dowolnie dużej skończonej liczby.
A to przecież nie wszystko, co matematyka na temat nieskończoności ma do powiedzenia. Wręcz przeciwnie - to dopiero marny wycinek, jako że teoria zbiorów przekonuje nas, że tak naprawdę rodzajów nieskończoności jest... nieskończenie wiele :) Uff, jak dobrze, że w informatyce wszystko, na czym się w praktyce operuje jest z założenia skończone i możliwe do policzenia... A jeśli nawet nie, to przecież zawsze można się ograniczyć do jakiegoś ograniczonego z góry zbioru wartości, które wspieramy. Tak jak pewien protokół używany do routowania, dla którego nieskończoną liczbą jest już... 16 :]

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
 



© 2010 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.