Przedziały, liczby i wstęp do matematyki w Rubim
(8 komentarzy)W kategoriach: Ruby , Ruby tutorial , Techblog / 13 maja 2007 [23:53:55]
Tagi technorati: Math Maths Numerical Programming Ruby Tutorial
Ruby w swoim standardowym wyposażeniu posiada bardzo dobre wsparcie dla matematyki, które wychodzi daleko poza standardowe funkcje, które znamy z modułów math innych języków programowania. Odkryjmy więc na nowo piękno matematyki - z Rubim.
Przedziały
Przedział jest obiektem standardowej klasy Range. Możemy je tworzyć na dwa sposoby:
a = 1..9 # => 1..9
b = 1...9 # => 1...9
Różnica między nimi jest taka, że dwie kropki tworzą przedział od-do prawostronnie domknięty, a trzy kropki - otwarty. Przekonajmy się, że to prawda:
a.to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9]
b.to_a # => [1, 2, 3, 4, 5, 6, 7, 8]
Przedziały tworzymy tak, że wartość mniejsza musi byc zawsze po lewej stronie:
(-1..2).to_a # [-1, 0, 1, 2]
(2..-1).to_a # []
Nic nie stoi na przeszkodzie, byśmy tworzyli przedziały ograniczone liczbami rzeczywistymi:
-2.5..4 # -2.5..4
Mechanika konwersji przedziałów nie pozwala na stworzenie tablicy z przedziału liczb zmiennoprzecinkowych - nieprawidłowe jest wywołanie (-2.5..4).to_a
Przedziały nieliczbowe
Ruby pozwala nam na tworzenie przedziałów nie tylko z liczb, ale i znaków i Stringów:
('a'..'z').to_a # ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
# "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
# "u", "v", "w", "x", "y", "z"]
("aa".."bf").to_a # ["aa", "ab", "ac", "ad", "ae", "af", "ag", "ah",
# "ai", "aj", "ak", "al", "am", "an", "ao", "ap",
# "aq", "ar", "as", "at", "au", "av", "aw", "ax",
# "ay", "az", "ba", "bb", "bc", "bd", "be", "bf"]
Przykłady
r1 = 'a'..'z' # => "a".."z"
r2 = 1..10 # => 1..10
r3 = 1...10 # => 1...10
r1.min # => "a"
r2.max # => 10
r3.max # => 9
r3.end # => 10
r1.include? 'c' # => true
r2.include? 5 # => true
r2 === 5 # => true
r2.include? 12 # => false
r1.each { |c| print c } # => "a".."z"
# Wyjście: abcdefghijklmnopqrstuvwxyz
Przedziały logiczne
Poświęciłem im cały wpis. Warto zajrzeć, gdyż konstrukcja posiada całkiem ciekawe zastosowania.
Liczby w Rubim
Język Ruby obsługuje wszystkie mniej lub bardziej standardowe typy liczbowe. Prześledźmy parę przykładów:
a = 5 # => 5
a.class # => Fixnum
a/2 # => 2
a%2 # => 1
b = a/2.0 # => 2.5
b.class # => float
b.round # => 3
b.floor # => 2
b.ceil # => 3
(-1.25).abs # => 1.25
1.zero? # => false
0.zero? # => true
3.between? 1, 5 # => true
3.between? 6, 10 # => false
# Potęgujemy
2**16 # => 65536
# Konwersje
5.to_s # => "5"
(-2.4).to_s # => "-2.4"
"-2.4".to_i # => "-2"
"-2.4".to_f # => "-2.4"
# Największy wspólny dzielnik
120.gcd 72 # => 24
# Najmniejsza wspólna wielokrotność
6.lcm 9 # => 18
# Jedno i drugie:
6.gcdlcm 9 # => [3, 18]
# Sposoby zapisu liczb
# Dziewięc milionów sto dwadzieścia pięć tysięcy pięćset siedemnaście
a = 9125517 # => 9125517
# Da się czytelniej:
a = 9_125_517
# Różne systemy liczbowe
a = 0x6c # => 108
b = 0o72 # => 58
b = 072 # => 58
c = 0b10011001 # => 153
Dzielimy przez zero
Dobrze widzisz. Przez zero. O ile działanie to na typach całkowitych jest niewykonalne, o tyle na floatach daje całkiem "normalny" wynik:
a = 1.0/0 # => Infinity
a.class # => float
a.infinite? # => 1
(-1.0/0).infinite? # => -1
a + 5 # => Infinity
a/5 # => Infinity
1/a # => 0
a/a # => NaN
r = -a..5 # => -Infinity..5
r.include? 3 # => true
A co z dużymi liczbami?
Nic. Programujesz w Rubim, nie masz się czym stresować. Nie wierzysz?
a = 2**128 # => 340282366920938463463374607431768211456
a.class # Bignum
a = a - 1 # => 340282366920938463463374607431768211455
Liczby zespolone
Biblioteka standardowa Rubiego daje nam bardzo wygodne wsparcie dla liczb zespolonych.
require 'complex' # => true
a = Complex(1, 3) # => Complex(1, 3)
b = Complex(-1.5, 5.17) # => Complex(-1.5, 5.17)
# Synonim:
a = 1 + 3.im # => Complex(1, 3)
2.im # => Complex(0, 2)
a.real # => 1
a.image # => 3
a.conj # => Complex(1, -3)
a.abs # => 3.16227766016838
# kwadrat modułu
a.abs2 # => 10
# argument w radianach
a.angle # => 1.24904577239825
a.polar # [3.16227766016838, 1.24904577239825]
a.to_s # => "1+3i"
a + b # => Complex(-0.5, 8.17)
a/b # => Complex(0.483455203613664, -0.333691064878239)
Complex::I**2 # => Complex(-1, 0)
Math::E**(Math::PI*Complex::I)
# => Complex(-1.0, 1.22460635382238e-016)
# Zwrócmy uwagę na pewną niedokładność części urojonej
Moduł Math
Moduł ten znamy z wielu innych języków programowania. Pozwala liczyć pierwiastki arytmetyczne, logarytmy naturalne, dziesiętne, eksponenty, funkcje trygonometryczne, arcusy, funkcje hiperboliczne i arcusy tychże. Oprócz tego zawiera parę ciekawych metod (ri!), w tym hypot:
include Math # => Object
hypot(3, 4) # => 5.0
Metoda zwraca oczywiście długość przeciwprostokątnej trójkąta o danych dwóch przyprostokątnych.
Rozwiązujemy układ równań liniowych
Rozwiążmy następujący układ równań:
a + 2b = 3 4a + 5b = 6
W Rubim mamy do tego gotowe metody:
require 'mathn' # => true
m = Matrix[[1, 2], [4, 5]] # => Matrix[[1, 2], [4, 5]]
y = Matrix.column_vector([3, 6]) # => Matrix[[3], [6]]
(m.inv*y) # => Matrix[[-1], [2]]
I mamy rozwiązanie. a = -1, b = 2. Prawda, że proste? :-)
Przyznam, że w niedalekiej przyszłości planuję napisać nieco więcej o matematycznych możliwościach Rubiego. Bo są one imponujące - odczułem to na własnej, realizując w nim projekt do obliczeń numerycznych w elektronice.
RaVbaker
14 maja 2007, 00:13:42Szkoda, że IRBa nie można na maturę ze sobą zabrać... :/ Jutro z matematyki właśnie.
Pozdrawiam, stały czytelnik kursu.
jpc
14 maja 2007, 00:25:15Żeby się nie obyło bez krytyki:
1. hypot = lambda a, b: abs (a + i*b) ;-)
2. Ciekawe, że jest to w stdlib w Ruby (nie znam chyba drugiego takiego języka ;), choć z drugiej strony odwracanie macierzy to dość kiepska metoda na rozwiązywanie równań macierzowych. ;-)
Btw. Jest dla Ruby coś porównywalnego z numpy?
3. Rozróżnienie między przedziałem ‘..’ i ‘...’ jest powalającym pomysłem. Jedynym gorszym mogłoby chyba być dodanie jeszcze przedziału konstruowanego za pomocą ‘....’. ;-)
Ala ogólnie miły wpis. :)
Pozdrawiam,
miłośnik Pythona i Scheme ;)
jpc
14 maja 2007, 00:27:15Ok, troche pomarudziłem, potem przeczytałem notkę o autorze i wtem nasunęło mi się pytanie: jaki wydział, kierunek i rok studiów. ;-)
Radarek
14 maja 2007, 10:42:58(Komentarz zmodyfikowany 14.05.2007 o 20:38)
Wpis bardzo fajny, jak zwykle :). Przydałby się jednak jeszcze jakiś przykładowy program, taki z życia wzięty :).
Hm, co w tym złego? Czepiający się Pythonista?:P
Co do przedziałów (Range) dodam, że można bardzo łatwo tworzyć klasy, które się tak zachowują:
ps. czemu nie ma podglądu komentarzy?:)
Hoppke
14 maja 2007, 14:21:43Te kropki faktycznie nie są specjalnie udane. Czytelność może być dość niska (o ile różnicę między „>=” a „>” widać w kodzie dość wyraźnie, o tyle „..” może niewiele się różnić od „...”). To kwestia fontów głównie, ale mimo wszystko…
Poza tym to nieintuicyjne. Gdyby ktoś mnie zapytał czym się różni „..” od „...”, to do głowy by mi nie przyszło, że „...” jest dłuższe i dlatego opisuje przedziały niedomknięte, a „..” jest krótsze i opisuje krótsze przedziały domknięte :)
Ale nie mam pojęcia jaki sposób byłby naprawdę lepszy.
RazorJack
14 maja 2007, 22:23:56JPC:
Biblioteka standardowa Rubiego posiada wiele ciekawych możliwości (dobre wsparcie dla macierzy, rozkłady LU [a-ha! da się bez odwracania macierzy], różniczkowanie numeryczne, jakobiany, algorytm Newtona-Raphsona itp.). Widziałem w sieci sporo bibliotek, które oferują bardziej wyrafinowane metody analizy numerycznej, przetwarzania sygnałów itp. Nie znam NumPy, ale z tego, co o nim czytałem, to nie wyprzedza tego, co może Ruby ze swoją artylerią. Temat obliczeń numerycznych mnie zainteresował, więc na pewno napiszę jeszcze co najmniej dwa artykuły o takim zastosowaniu Rubiego.
A co do moich studiów – Politechnika Łódzka, Wydział Elektrotechniki, Elektroniki, Informatyki i Automatyki, kierunek: Elektronika i Telekomunikacja, sem 4 :).
Radarek: Dodałem podgląd. Dzięki za motywację :).
Hoppke: Może i takie kropki nie są zbyt intuicyjne, ale, jak słusznie zauważyłeś, ciężko o coś lepszego. Nawet to, co znamy z matematyki, np. [5, 10) jest czymś czysto umownym i nieczytelnym dla kogoś, kto w temacie nie siedzi.
jpc
15 maja 2007, 00:31:38No popatrz, jaki świat mały. Ja to samo, ale na 6 semestrze. ;-)
Moglibyśmy się na piwo umówić, ale to takie odrobine kłopotliwe, ponieważ piwa nie pijam. ;-)
A’propos kropek: są nieczytelnie i już. Coś jak iteracja po stringach w Pythonie, niby fajne, ale w praktyce zbyt często daje bardzo dziwne błędy, które trudno wykryć, bo wydaje się, że tam w tym forze na pewno jest lista a nie nic innego (czyli ten string właśnie). Nie ma to związku z umownością, bo umowa matematyczna może jest arbitralna, ale przynajmniej trudno pomylić jeden nawias z drugim (chyba, że to kolokwium i student, niewiedząc, o który mu chodzi, pisze niewyraźnie ;).
RazorJack
19 maja 2007, 00:28:51Ulepszyłem artykuł. Dodałem do przykładu z liczbami zespolonymi kod ze sprzężeniem, argumentem itp, poza tym rozbudowałem przykład z zapisem liczb. Czegoś mi po prostu brakowało ;).
jpc: Moje namiary masz w sidebarze, więc… :>