Silnia i powiązane zagadnienia
Silnia to jedna z szerzej znanych funkcji matematycznych. Z jednej strony podczas nauki matematyki kojarzymy ją mocno z kombinatoryką, z drugiej podczas nauki programowania stanowi sztandarowy przykład rekurencji (zaraz obok ciągu Fibonacciego). Ten temat jednak warto rozszerzyć, bo wychodząc poza te dwa konteksty, trafimy na całkiem ciekawe zagadnienia matematyczne.
Silnia
Definicja
Zacznijmy od definicji silni, chociaż miałem już okazję opisać ją w artykule o rekurencji. Jednak nic nie szkodzi powtórzyć.
Silnią (po ang. factorial) liczby naturalnej nazywamy iloczyn wszystkich liczb naturalnych dodatnich od 1 do . Oznaczamy ją symbolem , a obliczamy następująco:
Możemy to oczywiście uprościć:
Warto wspomnieć, że powyższe wzory nie biorą pod uwagę specjalnego przypadku zera, które należy do dziedziny funkcji silnii (aczkolwiek nie należy do jej przeciwdziedziny, tam są tylko liczby naturalne dodatnie):
We wstępie do artykułu wspomniałem o rekurencji. Oczywiście w ten sposób również możemy definiować silnię:
Jest jeszcze alternatywny rekurencyjny sposób obliczania. Bardziej skomplikowany obliczeniowo, ale warto o nim wspomnieć. Wygląda następująco:
Czyli, szybko podsumowując, kolejne wartości silnii obliczamy następująco:
Przy okazji warto zauważyć, jak szybko rosną wartości silni. Już dla przekroczyliśmy 3 miliony. Oznacza to tyle, że jako programiści musimy brać pod uwagę, że szybko może dojść do przepełnienia typu liczbowego. W przypadku 32-bitowego typu całkowitego największą liczbą, dla której możemy obliczyć silnię, jest zaledwie . W przypadku 64-bitowego jest nie lepiej, bo zaledwie .
Implementacja
Czas na część programistyczną, która jest absolutnie najnudniejszą częścią tego artykułu. Dlaczego tak? Bo obliczanie silni z definicji to szkolny problem i nie ma tu większej filozofii. Poniżej pokażę pięć sposobów w JavaScript wykorzystujące nieograniczony typ liczbowy BigInt, dzięki któremu obliczymy silnię dla znacznie większych liczb, jednak kosztem wolniejszych obliczeń.
Najpierw iteracyjnie:
function factorial(n) {
let result = 1n;
for (let i = 1n; i <= BigInt(n); i++) {
result *= i;
}
return result;
}
Teraz rekurencyjnie:
function factorial(n) {
n = BigInt(n);
if (n === 0n) {
return 1n;
}
return n * factorial(n - 1n);
}
Drugi rekurencyjny wzór możemy zderekursywować, korzystając z programowania dynamicznego:
function factorial(n) {
const dp = new Array(n + 1).fill(0);
dp[0] = 1n;
dp[1] = 1n;
for (let i = 2; i <= n; i++) {
dp[i] = (BigInt(i) - 1n) * (dp[i - 1] + dp[i - 2]);
}
return dp[n];
}
Następnie możemy go zoptymalizować, aby zamiast liniowej złożoności pamięciowej, miał stałą:
function factorial(n) {
let prev2 = 1n;
let prev1 = 1n;
for (let i = 2; i <= n; i++) {
const curr = (BigInt(i) - 1n) * (prev1 + prev2);
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
Zróbmy jeszcze generator, który będzie zwracać kolejne wartości silni:
function* factorial() {
let result = 1n;
let i = 1n;
while (true) {
result *= i;
yield result;
i++;
}
}
Ostatnia wersja jest o tyle ciekawa, że zawsze wykorzystuje poprzednią wartość silni do obliczenia kolejnej, więc jest najbardziej efektywna obliczeniowo.
Wszystkie trzy implementacje zamieściłem na Replit: iteracyjna, rekurencyjna, programowanie dynamiczne, programowanie dynamiczne zoptymalizowane, generator. Możesz sobie je tam przetestować.
Dodatkowo przy wersji z generatorem dodałem pętlę, która wypisuje kolejne wartości silni w nieskończoność, aby zobaczyć, kiedy obliczenie będzie trwać zbyt długo. Odpalając na Replit, ostatnią wartość obliczyło mi dla .
Zera na końcu wartości
Jeśli uruchomiłeś(-aś) pokazane przeze mnie przykłady w kodzie, to mogłeś(-aś) zauważyć pewną rzecz — im większa silnia, tym więcej zer jest na końcu. Można przez to pomyśleć, że straciliśmy precyzję typu liczbowego.
Otóż nic bardziej mylnego. „Zyskiwanie” zer na końcu to jedna z właściwości silni. Wynika z faktu, że wartości silni (pomijając i ) są liczbami parzystymi, więc każdorazowe przemnożenie ich przez wielokrotność piątki powoduje dorzucenie zera na koniec.
Tak się składa, że liczbę zer możemy obliczyć. Dość intuicyjne wydawałoby się, że wystarczyłoby zrobić . Niestety, to obliczenie będzie prawdziwe jedynie do ( ma 6 zer na końcu, a nie 5). Prawidłowy wzór na obliczenie wygląda następująco:
Możemy stąd policzyć, że dla będziemy mieć:
, więc jak widzimy, wszystko się zgadza.
Zastosowania
Bardzo nietypowo jak na moje artykuły, gdzie zastosowania zwykle piszę na końcu, opowiedzmy sobie o nich teraz. Też z góry ostrzegam, że nie będę wchodzić w głębokie matematyczne obszary znane tylko nielicznym. Pokażę tylko te, które sam pamiętam z toku swojej edukacji.
Kombinatoryka
Przede wszystkim funkcja silni najczęściej kojarzona jest z kombinatoryką. Najbardziej oczywista jest tutaj permutacja bez powtórzeń (liczba sposobów na ułożenie wszystkich elementów zbioru), bo jest to dosłownie silnia: . Mamy jeszcze:
- Permutację z powtórzeniami (liczba sposobów na ułożenie wszystkich elementów zbioru, gdy te mogą się powtarzać): .
- to liczba wszystkich elementów, a to liczba elementów bez powtórzeń. W mianowniku podajemy liczbę powtórzeń każdego z elementów.
- Wariację bez powtórzeń (ile różnych k-elementowych ciągów możemy ułożyć z n elementów zbioru): .
Na tym jednak nie kończmy. Mamy jeszcze symbol Newtona, czyli:
Na jego podstawie zostały zbudowane kolejne wzory z kombinatoryki:
- Kombinacja bez powtórzeń (liczba k-elementowych podzbiorów zbioru n-elementowego): .
- Kombinacja z powtórzeniami (to, co wyżej, ale zakładając, że elementy w pozdbiorach mogą się powtarzać): .
Symbol Newtona poza kombinatoryką
Symbol Newtona to jednak nie tylko kombinatoryka. Popularne są jeszcze jego trzy inne zastosowania:
- Trójkąt Pascala — możemy za jego pomocą zbudować trójkąt Pascala. Chociaż raczej wykorzystanie jest odwrotne — na podstawie trójkąta Pascala oblicza się w prosty sposób wartości symbolu Newtona.
- Dwumian Newtona — inna nazwa symbolu Newtona to współczynnik dwumianowy. Bierze się to stąd, że za jego pomocą możemy obliczyć dwumian Newtona, z którego to wylicza się popularne wzory skróconego mnożenia. Wzór jest następujący: .
- Krzywe Béziera — służy do obliczania współrzędnych punktów na szeroko stosowanych w grafice komputerowej krzywych Béziera.
Wzór Taylora
Ostatnie zastosowanie, które chciałem opisać, pochodzi już z uniwersyteckiej matematyki (analiza matematyczna) — wzór Taylora, a także bazujący na nim i wykorzystywany w informatyce szereg Maclaurina.
Bez wchodzenia w zawiłe szczegóły — Brook Taylor udowodnił w 1715 r., że funkcje, które możemy różniczkować, możemy zapisać za pomocą wielomianu zależnego od kolejnych pochodnych tychże funkcji. Nie chcę głęboko wchodzić w meandry tego twierdzenia, jak i pokazywać samego wzoru Taylora, więc zróbmy dość duży przeskok...
Uproszczeniem wzoru Taylora jest szereg Taylora. Wygląda następująco:
We wzorze tym to n-ta pochodna funkcji , a to pierwszy wyraz dziedziny funkcji. Jeśli nie będziemy sumować nieskończenie, a jedynie do wybranej wartości , uzyskamy przybliżoną wartość funkcji — jest to główne zastosowanie tego wzoru.
Zakładając, że , możemy wyprowadzić jeszcze prostszy wzór — szereg Maclaurina:
Dlaczego wzory te są tak istotne? Ponieważ kolejne pochodne wielu popularnych funkcji sprowadzają się do podstawowych operacji matematycznych. Dzięki temu jesteśmy w stanie dość precyzyjnie obliczyć, np. wartość funkcji sinus dla dowolnej liczby, a nie tylko dla odgórnie zdefiniowanych przez tablice matematyczne. Stąd też ich szerokie zastosowanie w informatyce — dokładnie te wzory kryją się pod wszelkimi Math.sin()
, Math.cos()
itd.
A skoro o sinusie mowa, to tak zapisalibyśmy go w postaci szeregu Maclaurina (po uproszczeniu wzoru):
Przykładowe zadanie z silnią
Powyższe zastosowania silni pokazują, dlaczego funkcja silni jest tak istotna. Co więcej, wyczerpują też większość możliwych zadań matematycznych z nią związanych. Aczkolwiek żeby zrozumieć lepiej, jak silnia jest obliczana, zaproponuję mniej praktyczne zadanie, jednak dość ciekawe z punktu widzenia zrozumienia definicji. Jest wzorowane na zadaniu z Kangura Matematycznego 2023 (poziom student, zad. 12), ale nie jest identyczne.
Znajdź , dla którego prawdziwa jest równość: .
Postaraj się obliczyć to zadanie bez mnożenia tych dwóch silni (bo to byłoby zbyt proste).
Kliknij tutaj aby zobaczyć rozwiązanie
Wynik to . Jak do tego doszliśmy?
Jak pamiętamy, silnia to iloczyn kolejnych liczb naturalnych. Stąd . W takim razie musimy znaleźć, iloczyn ilu kolejnych liczb po 7 da nam .
W tym miejscu musimy jednak obliczyć . Jest to dość proste i nie wymaga kalkulatora, więc nawet na konkursie matematycznym powinniśmy sobie z tym poradzić. Wynik to . Teraz musimy tylko znaleźć kolejne liczby po 7:
- jest oczywiście mniejsze niż ,
- , dalej jesteśmy poniżej ,
- .
Skoro , to , więc .
Na tej zagadce matematycznej zakończmy już podstawowy opis silni i przejdźmy do bardziej zaawansowanych tematów.
Funkcja gamma
Silnia charakteryzuje się tym, że jest zdefiniowana tylko dla nieujemnych liczb całkowitych. Czy jednak istnieje funkcja, która rozszerza tę definicję na liczby rzeczywiste (lub nawet zespolone)?
Tak! Jest nią funkcja gamma (oznaczana jako ). Jest zdefiniowana dla wszystkich liczb zespolonych z wyłączeniem ujemnych liczb całkowitych i zera.
(źródło: Alessio Damato, CC BY-SA 3.0, via Wikimedia Commons)
Definicja
Istnieje kilka sposobów definiowania funkcji gamma. Najpopularniejsza jest definicja całkowa, która wymaga, aby część rzeczywista liczby zespolonej była dodatnia:
Tutaj tylko dopowiem, jeśli nie wiesz, czym są liczby zespolone: w skrócie, są to liczby składające się z dwóch części — rzeczywistej i urojonej. Zapisuje się je często w postaci algebraicznej wyglądającej następująco: , gdzie to część rzeczywista, to część urojona, a to jednostka urojona, dla której zachodzi równość . Absolutnie nie musisz się tym teraz przejmować, bo nie będziemy wchodzić w szczegóły liczb zespolonych. Za to interesuje nas to, że jeśli , to liczba zespolona jest zwykłą liczbą rzeczywistą.
Powyższy wzór może na to nie wskazywać, jednak za jego pomocą jesteśmy w stanie wyznaczyć wartości silni dla liczb naturalnych dodatnich, a także interpolować wartości między nimi. Dokładniej, dla każdej liczby naturalnej dodatniej zachodzi równość:
Istnieje jeszcze jedna definicja funkcji gamma, która pozwala na rozszerzenie jej dziedziny na wszystkie liczby zespolone z wyłączeniem ujemnych liczb całkowitych i zera. Jest to definicja iloczynowa:
Obliczanie wartości
Wiem, że całka może wyglądać strasznie, szczególnie że pojawia się jakieś . Więc jak obliczać wartości tej funkcji?
Tutaj pojawia się duży szkopuł — nie istnieje prosty sposób na obliczenie wartości tej całki dla dowolnej liczby zespolonej. Tak samo, korzystając z definicji iloczynowej, nie damy rady mnożyć w nieskończoność. Jest jednak kilka właściwości, które możemy wykorzystać do obliczenia wartości funkcji gamma dla konkretnych liczb. Pominę ich wyprowadzenie, od razu dam końcowe wzory.
Najważniejszą z nich jest rekurencyjna własność funkcji gamma:
Załóżmy, że chcemy obliczyć . Wykorzystując powyższą własność, możemy to zrobić następująco:
Jak widzimy, obliczyliśmy wartość bez konieczności rozwiązywania całki i w zasadzie doszliśmy do definicji silni.
Jednak co z liczbami rzeczywistymi, które nie są całkowite? Łatwo możemy obliczyć liczby podzielone na 2. Tutaj wykorzystujemy inne własności funkcji gamma:
Przykładowo, aby obliczyć , możemy skorzystać z pierwszego wzoru, gdzie :
Wciąż nie jest to jednak pełny zakres liczb rzeczywistych, a tym bardziej zespolonych. Aby poradzić sobie z tym problemem, wartości funkcji gamma aproksymuje się za pomocą różnych metod numerycznych.
Zastosowania
Teraz możesz się zastanawiać — po co ta funkcja istnieje? Jakie ma zastosowania?
Najczęściej funkcję gamma możemy spotkać w statystyce. Jest wykorzystywana do definiowania różnych rozkładów prawdopodobieństwa, takich jak rozkład gamma, chi-kwadrat czy t Studenta. Stąd też powszechnie znajdziemy ją w bibliotekach statystycznych i narzędziach do analizy danych.
Oprócz tego znajdziemy zastosowania w wielu innych dziedzinach matematyki (i nie tylko). Przykładowo, dla liczb zespolonych , dla których , znajdziemy następujący wzór:
Co ciekawe, zastosowanie funkcji gamma znajdziemy nawet w geometrii. Jeśli chcielibyśmy uogólnić wzór na objętość kuli do wymiarów, wzór ten wygląda następująco:
Aproksymacja wartości silni
Wartości silni rosną bardzo szybko, więc dla większych liczb staje się niepraktyczna do obliczeń. Stosując standardowe typy liczbowe w programowaniu, szybko napotykamy problem przepełnienia. Nawet jeśli używamy typów o dużym zakresie, takich jak BigInt w JavaScript, obliczenia stają się coraz wolniejsze i bardziej zasobożerne. Jednak mimo to serwisy takie jak Wolfram|Alpha są w stanie obliczyć silnię dla bardzo dużych liczb wręcz momentalnie. Jak to możliwe?
Otóż mamy kilka sposobów na przybliżenie wartości silni bez konieczności jej dokładnego obliczania. Zobaczmy je.
Wzór Stirlinga
Najbardziej znanym przybliżeniem wartości silni jest wzór Stirlinga, nazwany tak po Jamesie Stirlingu, aczkolwiek podwaliny pod jego powstanie opracował wcześniej Abraham de Moivre.
Pomijając wyprowadzenia, mamy dwa powiązane ze sobą wzory:
Nas oczywiście interesuje pierwszy wzór, ale o wzorze na logarytm warto wspomnieć, bo też jest często spotykany.
Wzór Stirlinga jest dość dokładny, a jego dokładność rośnie wraz z wartością . Przykładowe pierwsze wartości wyglądają następująco:
Kod obliczający te wartości znajdziesz na Replit.
Jak widzimy, nawet dla małych liczb przybliżenie jest całkiem dobre. Dla większych liczb błąd staje się wręcz pomijalny. Możemy to udowodnić matematycznie następującą granicą:
Przybliżenie Stirlinga dla funkcji gamma
Jak pamiętamy z poprzedniego rozdziału, funkcja gamma rozszerza definicję silni na liczby rzeczywiste i zespolone. Dla dodatnich liczb całkowitych prawdziwa jest równość . Czy wzór Stirlinga możemy zastosować do funkcji gamma? Okazuje się, że tak.
Stosując wprost wzór Stirlinga do funkcji gamma, otrzymujemy:
Jak widzimy, przybliżenie jest całkiem dobre, i tak jak w przypadku silni — dokładność rośnie wraz z wartością argumentu. Przy obliczaniu należy jednak pamiętać, że obliczając wartość funkcji gamma dla liczby , musimy wstawić do wzoru Stirlinga.
Przybliżenie Lanczosa
Mimo że przybliżenie Stirlinga jest całkiem dobre, to dla małych liczb błąd jest zauważalny. Na szczęście istnieje inne przybliżenie, które jest znacznie dokładniejsze — przybliżenie Lanczosa, opracowane przez Corneliusa Lanczosa w 1964 r. Jest nieco bardziej skomplikowane obliczeniowo (wzór Stirlinga ma stałą złożoność obliczeniową, a Lanczosa już nie), ale za to bardzo dokładne nawet dla małych liczb. Z tego też powodu jest standardem obliczania funkcji gamma w wielu bibliotekach matematycznych.
Wzór Lanczosa wygląda następująco dla funkcji Gamma:
gdzie (już po uproszczeniu):
W powyższych wzorach to stała (zwykle mała liczba całkowita), natomiast to współczynniki zależne od (stąd alternatywnie są zapisywane jako ), które są z góry ustalone. Aby nie komplikować artykułu, nie będę pokazywać, w jaki sposób je wyliczyć, ale możemy przykładowe wartości bez problemu znaleźć w różnych źródłach (np. w kodzie, który zalinkuję kawałek niżej, będą przykładowe wartości). Zwykle wystarczy ok. 10 współczynników, aby uzyskać bardzo dobrą dokładność.
To teraz zobaczmy, jak wzór Lanczosa poradzi sobie z obliczaniem wartości silni i funkcji gamma (dla i 9 współczynników):
Powyższe obliczenia wykonałem w kodzie udostępnionym na Replit. Jak widzisz, są niemal idealne. O ile tutaj wyglądają w punkt, tak na załączonym Replit możesz sprawdzić, że błąd jest, ale na odległych miejscach po przecinku.
Silnia wielokrotna
Załóżmy, że dostajesz zadanie obliczenia następującej wartość:
Co w tym momencie robisz? Pierwsze skojarzenie to prawdopodobnie obliczyć , a potem jeszcze raz silnię z wyniku. Czyli wyszłoby, że wynikiem jest (odpuśćmy dokładne obliczenie). Jednak jest to błąd. Przez zwiększanie liczby wykrzykników w zapisie (bez nawiasów) mamy do czynienia z silnią wielokrotną, którą obliczamy nieco inaczej.
Silnia podwójna
W przypadku dwóch znaków wykrzyknika (tzw. podwójna silnia) obliczamy iloczyn liczb, które są o dwa mniejsze od poprzedniej. Formalnie definicja wygląda następująco:
Stąd jeśli chcemy obliczyć , to:
Silnie k-te
Nie musimy się jednak zatrzymywać na dwóch wykrzyknikach. Możemy mieć ich dowolną liczbę, co możemy skrócić do formy zapisu (lub — zależy od źródła), gdzie to liczba wykrzykników. W takim przypadku definicja wygląda następująco:
Więc gdy chcemy obliczyć (silnia potrójna), wygląda to następująco:
Silnie wielokrotne dla liczb ujemnych
Jak pamiętasz, silnia jest zdefiniowana tylko dla liczb całkowitych nieujemnych. Widzieliśmy to także przy okazji funkcji gamma, gdzie mimo rozszerzenia dziedziny na liczby rzeczywiste i zespolone nadal nie jest zdefiniowana dla liczb całkowitych ujemnych i zera. Jednak silnia wielokrotna może zostać obliczona dla całej dziedziny liczb całkowitych, z wyjątkiem pewnych wartości zależnych od . Aby to umożliwić, musimy zmienić definicję silni wielokrotnej:
Warunek w ostatniej linii mówi o tym, że nie możemy obliczyć silni wielokrotnej dla liczb całkowitych ujemnych, które są wielokrotnościami (czyli np. dla nie możemy obliczyć silni podwójnej dla itd.).
Przykładowo, jeśli chcemy obliczyć , to:
Przybliżenie silni podwójnej
Wracając do silni podwójnej, bo ta jest najpopularniejsza z silni wielokrotnych, istnieje wzór na jej przybliżenie (dla dodatnich ). Bazuje na wzorze Stirlinga i wygląda następująco:
Skoro obliczaliśmy wcześniej , sprawdźmy na niej, jak działa to przybliżenie:
Podobnie jak przy oryginalnym wzorze Stirlinga, im większa liczba, tym dokładniejsze przybliżenie.
Zastosowania
Pytanie brzmi, czy silnia wielokrotna ma jakieś zastosowania? Oczywiście, że tak. Nie chcę jednak wyszukiwać zbyt daleko w matematycznych zagadnieniach, z którymi nikt poza studentami matematyki nie ma do czynienia. Dlatego odwołam się do czegoś, co już wcześniej opisywałem — funkcji Gamma.
Pokazałem wyżej wzory na obliczanie dokładnych wartości funkcji gamma dla liczb postaci oraz . Wzory te możemy zapisać także za pomocą silni podwójnej, otrzymując nieco prostsze wyrażenia:
Stąd bardzo często, gdy spotykamy się z zastosowaniami silni podwójnej, tak naprawdę mamy do czynienia z funkcją gamma.
Potęgi kroczące
Kolejne pojęcie, które chciałem przedstawić, na pierwszy rzut oka nie brzmi jak coś związanego z silnią. Potęgi kroczące to dość specyficzne symbole matematyczne oznaczające pewne wielomiany. Gdzie tu powiązanie z silnią? Otóż mamy dwa rodzaje potęg kroczących: silnię górną i silnię dolną. Obie przypominają w obliczaniu silnię i także możemy je zdefiniować za pomocą zwykłej silni.
Silnia górna
Pierwszym rodzajem potęgi kroczącej jest silnia górna (ang. rising factorial), oznaczana jako (w zagranicznych źródłach spotkamy też ). Czytamy to jako „x do n-tej przystającej”. Definicja wygląda następująco:
Dodatkowo dla przyjmujemy, że .
W intuicyjny sposób moglibyśmy powiedzieć, że jest to silnia, w której zamiast mnożyć kolejne liczby naturalne, mnożymy kolejnych liczb, zaczynając od i zwiększając o 1.
Zobaczmy to na przykładzie. Jeśli chcemy obliczyć , wygląda to następująco (na dwa sposoby):
Wspomniałem, że są to charakterystyczne wielomiany, ale na przykładzie z konkretnym tego nie widać. Spójrzmy więc, jak to wygląda, jeśli nie podstawimy nic za :
Silnia dolna
Drugi rodzaj to silnia dolna (ang. falling factorial), oznaczana jako (w zagranicznych źródłach spotkamy też ). Czytamy to jako „x do n-tej ubywającej”. Definicja wygląda następująco:
Analogicznie jak wcześniej, dla przyjmujemy, że .
Tym razem moglibyśmy powiedzieć, że jest to silnia, w której zamiast mnożyć kolejne liczby naturalne, mnożymy kolejnych liczb, zaczynając od i zmniejszając je o 1.
Ponownie zobaczmy przykład liczbowy. Jeśli chcemy obliczyć , wygląda to następująco (na dwa sposoby):
Zobaczmy też rozwinięcia w wielomiany:
Rozszerzenie na liczby rzeczywiste
Powyżej zdefiniowaliśmy potęgi kroczące tylko dla liczb naturalnych, analogicznie jak dla zwykłej silni. Aczkolwiek skoro silnię byliśmy w stanie rozszerzyć na liczby rzeczywiste i zespolone za pomocą funkcji gamma, to czy potęgi kroczące również możemy? Okazuje się, że tak. Wystarczy silnię zastąpić funkcją gamma:
Pominę tutaj konkretne obliczenia, ale możesz łatwo zauważyć, że dla liczb naturalnych dodatnich powyższe wzory dają dokładnie te same wyniki co wcześniejsze definicje. Musimy jednak pamiętać, że funkcja gamma nie jest zdefiniowana dla liczb całkowitych ujemnych i zera, więc potęgi kroczące dla konkretnych wartości oraz także nie będą zdefiniowane.
Rozszerzenie na ujemne wykładniki
A może jednak moglibyśmy rozszerzyć potęgi kroczące na ujemne wykładniki całkowite? Okazuje się, że możemy rozszerzyć definicję silni dolnej w ten sposób. Wówczas definicja wygląda następująco:
Dzięki temu możemy obliczyć np. :
Zastosowania
Jedno z zastosowań silni dolnej miałem okazję już wcześniej pokazać przy zastosowaniach silni. Pamiętasz wariację bez powtórzeń? Możemy ją uprościć do postaci potęgi kroczącej:
Obie potęgi kroczące znajdziemy także w innym obszarze kombinatoryki — przy obliczaniu liczb Stirlinga. Te jednak pominę, aby nie komplikować jeszcze bardziej artykułu.
Zastosowania znajdziemy też w innych, bardziej zaawansowanych dziedzinach matematyki. W matematyce dyskretnej z potęgami kroczącymi spotkamy się w rachunku różnic skończonych. Oprócz tego potęgi kroczące pojawiają się w teorii hipergeometrycznej, a także w teorii specjalnych funkcji matematycznych. Mimo że rachunek różnic skończonych poznaje się na studiach informatycznych, to jednak nie jest to temat do opowiedzenia przy okazji silni. Z hipergeometrią natomiast nigdy nie miałem okazji się spotkać podczas swojej edukacji, ale znajduje zastosowania w mechanice kwantowej czy statystyce.
Podsilnia
Dowiedzieliśmy się nieco wcześniej, co znaczy, gdy powtórzymy wykrzyknik. Co jednak powiesz na poniższy zapis?
Proszę nie regulować odbiorników, to nie jest pomyłka. Wykrzyknik możemy zapisać także przed liczbą — wówczas mamy do czynienia z podsilnią (po ang. subfactorial).
Sposób obliczania
Podstawowy sposób obliczania podsilni to rekurencyjna definicja, według której obliczamy podsilnię tak samo jak silnię, tylko z tą różnicą, że , ale . Łatwo możesz zauważyć, że najpopularniejsza definicja silni dałaby wtedy za każdym razem wynik zerowy. Nie bez powodu jednak podałem drugi, bardziej skomplikowany obliczeniowo wzór, bo jego możemy wprost przenieść do definicji podsilni:
Korzystając z tego wzoru, możesz też od razu zrobić w prosty sposób implementację programistyczną — przypomnij sobie, jak zaimplementowaliśmy silnię przy użyciu programowania dynamicznego. Wystarczy tylko zmienić początkową wartość.
Mamy też definicje iteracyjne. Przykładowe z nich to:
Obliczmy sobie przykładowe wartości podsilni:
Wartości te są znacznie inne niż wartości silni, ale rosną podobnie szybko. Co ciekawe, stosunek podsilni do silni zbliża się do odwrotności liczby :
Oznacza to, że dla bardzo dużych podsilnia jest w przybliżeniu równa . Dlatego też możemy przybliżać podsilnię, korzystając ze wzorów do aproksymacji silni, które opisałem wcześniej, dzięląc następnie wynik przez .
Zastosowania
Pewnie już nie pierwszy raz zastanawiasz się w ciągu tego artykułu: „po co jest mi to potrzebne”. Podsilnia ma jedno bardzo specyficzne zastosowanie, czym chyba nie zaskoczę — w kombinatoryce.
Wykorzystujemy ją do obliczania liczby nieporządków zbioru skończonego, czyli permutacji, w których żaden element nie znajduje się na swojej pierwotnej pozycji. Przykładowo, jeśli mamy trzy elementy: A, B, C, to mamy następujące permutacje:
- ABC (żaden element nie zmienił pozycji)
- ACB (B i C zmieniły pozycje)
- BAC (A i B zmieniły pozycje)
- BCA (wszystkie elementy zmieniły pozycje)
- CAB (wszystkie elementy zmieniły pozycje)
- CBA (A i C zmieniły pozycje)
W powyższym przykładzie mamy 6 permutacji, z czego tylko 2 spełniają warunek nieporządku (BCA i CAB). I właśnie liczba takich permutacji to podsilnia , gdzie to liczba elementów w zbiorze. W naszym przypadku , więc .
Pierwsznia
Gładko przeszliśmy do ostatniego, związanego z silnią pojęcia, które chciałem opisać. Jest nim pierwsznia (po ang. primorial), oznaczana jako .
Definicja
Podstawowa definicja pierwszni to definicja dla liczb pierwszych. Mówi o tym, że dla n-tej liczby pierwszej pierwsznia to iloczyn początkowych liczb pierwszych:
Stąd jeśli chcemy obliczyć , wygląda to następująco:
Prawda jest jednak taka, że rzadko operujemy na indeksach liczb pierwszych. Dlatego też pierwsznia ma też drugą definicję dla liczb naturalnych, która wygląda następująco:
W powyższym wzorze to funkcja zliczająca liczby pierwsze.
Możemy też prościej zdefiniować rekurencyjnie pierwsznię:
W skrócie — mnożymy wszystkie liczby pierwsze mniejsze lub równe . Jeśli jest mniejsze niż 2, to przyjmujemy wartość 1 (bo nie ma liczb pierwszych mniejszych niż 2).
Stąd jeśli chcemy obliczyć , mnożymy wszystkie liczby pierwsze mniejsze lub równe 12:
Zastosowania
Pierwsznia, mimo że wydaje się ciekawostką w porównaniu do wszystkich wcześniejszych pojęć, znalazła dość ciekawe zastosowania w informatyce, szczególnie w kryptografii. Nie dziwi to, bo liczby pierwsze są fundamentem wielu algorytmów kryptograficznych. Zobaczmy przykładowe zastosowania algorytmiczne.
Szukanie liczb pierwszych
Szukając największych liczb pierwszych, naukowcy nie skupiają się na sprawdzaniu każdej liczby z osobna. Zamiast tego szuka się liczb według specyficznych wzorów, które są znane z tego, że często dają liczby pierwsze. Najbardziej znany jest tutaj wzór Mersenne'a, czyli . Więcej takich wzorów opisałem w artykule Duże liczby pierwsze. Nas zainteresuje jednak inny wzór, czyli liczby pierwsze bazujące na pierwszni (po ang. primorial prime).
Mamy dwa wzory:
Szukaniem liczb pierwszych spełniających te wzory zajmuje się przede wszystkim projekt PrimeGrid. W momencie pisania artykułu (wrzesień 2025 r.) największymi znanymi liczbami pierwszymi bazującymi na pierwszni są:
- ()
- ()
Sprawdzanie gładkości liczb
W matematyce mówimy, że liczba jest B-gładka (po ang. B-smooth), jeśli jej wszystkie czynniki pierwsze są mniejsze lub równe pewnej liczbie . Na przykład, liczba 60 jest 5-gładka, ponieważ . Natomiast 62 już nie jest 5-gładka, bo faktoryzujemy ją następująco: . Zastosowania liczb gładkich znajdziemy przy obliczaniu szybkiej transformacji Fouriera (FFT), w kryptoanalizie, a nawet w teorii muzyki.
Tylko jak się do tego mają pierwsznie? Aby sprawdzić, czy liczba jest B-gładka, możemy skorzystać z pierwszni . Jeśli dana liczba dzieli się przez , to jest B-gładka. W przeciwnym razie nie jest. Przykładowy algorytm sprawdzający, czy jest B-gładkie, wygląda następująco:
- Oblicz
k = B#
. - Oblicz największy wspólny dzielnik
n
ik
:g = NWD(n, k)
. - Jeśli
g == 1
, ton
nie jest B-gładkie. - Podziel
n
przezg
:n = n / g
. - Powtarzaj kroki 2-4, aż
g == 1
lubn == 1
. - Jeśli
n == 1
, ton
jest B-gładkie, w przeciwnym razie nie jest.
Idea tego algorytmu polega na tym, że jeśli n
ma jakikolwiek czynnik pierwszy większy od B
, to ten czynnik nie będzie dzielił B#
, więc największym wspólnym dzielnikiem w pewnym momencie stanie się 1. Do tego możemy założyć, że B
jest stałe między kolejnymi wywołaniami, stąd B#
możemy obliczyć raz i przechować.
Sito pierwszniowe
Innym zastosowaniem pierwszni są tzw. sita pierwszniowe do generowania liczb pierwszych. Są podobne do sita Eratostenesa, z tym że zamiast eliminować wielokrotności kolejnych liczb pierwszych, budujemy matrycę o nieskończonej wysokości, gdzie część kolumn oznaczamy jako podpory, które mają kandydatów na liczby pierwsze. Pozostałe kolumny zawierają w całości liczby złożone.
Poniżej możesz zobaczyć wizualizację -sita pierwszniowego. Nie wszystkie kolumny mieszczą się na ekranie, więc możesz przewinąć w prawo, aby zobaczyć więcej.
Na czarno oznaczone są kolumny podpór. Komórki w kolumnach podpór, oznaczone na czerwono, to liczby pierwsze. Jak widać, mamy tutaj czasami liczby złożone (oznaczone na biało). Natomiast poza kolumnami podpór mamy same liczby złożone, stąd je całkowicie ignorujemy (oznaczone na szaro). Jedynie w pierwszym wierszu wyeliminowaliśmy kilka liczb pierwszych (2, 3 i 5), bo są mniejsze niż największa liczba pierwsza w pierwszym wierszu.
Zrozummy, co to sito ma do pierwszni. Zacznijmy od tego, że powyżej widzimy -sito, czyli mające 30 kolumn (ponieważ ). Ma 8 podpór, ponieważ (funkcja Eulera, tocjent — przypisuje każdej liczbie naturalnej liczbę liczb względnie z nią pierwszych). A jakie liczby zostają podporami? Dokładnie te, które są względnie pierwsze z (uwaga, nie muszą to być liczby pierwsze). Warto też zauważyć, że nie musimy oczywiście rozpisywać całej matrycy, ponieważ kolejne liczby w kolumnie możemy obliczyć ze wzoru , gdzie to liczba w kolumnie, a to numer wiersza (liczony od zera).
Sito pierwszniowe będzie tym lepsze w generowaniu liczb pierwszych, im mniejszy będzie stosunek liczby podpór do liczby kolumn. W pokazanym wyżej przykładzie jest to . Dla będzie to już , a dla już tylko . Warto jednak wziąć pod uwagę, że przy będziemy mieli ponad 223 miliony kolumn, z czego około 36 milionów to podpory.
Podsumowanie
Dotarliśmy do końca artykułu. Czy opisałem wszystko? Absolutnie nie. Pominąłem takie, stosunkowo proste, zagadnienia, jak factorion, superfaktorial, hiperfaktorial czy silnię wykładniczą. Oprócz tego wiele zaawansowanych, jak niepełną funkcję gamma, q-silnię czy funkcję G Barnesa. Wszystko to jednak wymagałoby znacznie więcej miejsca i czasu, a artykuł i tak jest już bardzo długi.
Mam nadzieję, że udało mi się pokazać, że silnia to coś więcej niż szybko rosnące liczby i przykład funkcji rekurencyjnej, ale ma też wiele ciekawych zastosowań. A do tego, że są też z nią powiązane równie ciekawe i przydatne pojęcia.
Literatura
- Weisstein, Eric W. "Factorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/Factorial.html
- Wzór Taylora [online]. Wikipedia : wolna encyklopedia, 2025-09-07 15:06Z [dostęp: 2025-09-24 05:57Z]. Dostępny w Internecie: https://pl.wikipedia.org/w/index.php?title=Wz%C3%B3r_Taylora&oldid=77555919
- Kangur Matematyczny - Testy konkursowe https://www.kangur-mat.pl/testy_konkursowe.php#2023
- Weisstein, Eric W. "Gamma Function." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/GammaFunction.html
- Stirling's approximation, https://en.wikipedia.org/w/index.php?title=Stirling%27s_approximation&oldid=1308153547 (dostęp: 2025-09-24).
- Lanczos approximation, https://en.wikipedia.org/w/index.php?title=Lanczos_approximation&oldid=1239419319 (dostęp: 2025-09-24).
- Weisstein, Eric W. "Multifactorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/Multifactorial.html
- Weisstein, Eric W. "Double Factorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/DoubleFactorial.html
- Double factorial, https://en.wikipedia.org/w/index.php?title=Double_factorial&oldid=1309712607 (dostęp: 2025-09-24).
- Weisstein, Eric W. "Rising Factorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/RisingFactorial.html
- Weisstein, Eric W. "Falling Factorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/FallingFactorial.html
- Falling and rising factorials, https://en.wikipedia.org/w/index.php?title=Falling_and_rising_factorials&oldid=1305259865 (dostęp: 2025-09-24).
- Potęgi kroczące [online]. Wikipedia : wolna encyklopedia, 2020-12-18 17:49Z [dostęp: 2025-09-24 06:06Z]. Dostępny w Internecie: https://pl.wikipedia.org/w/index.php?title=Pot%C4%99gi_krocz%C4%85ce&oldid=61703519
- Weisstein, Eric W. "Subfactorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/Subfactorial.html
- Podsilnie i nieporządki – ignormatyk https://xpil.eu/podsilnie-i-nieporzadki/
- Weisstein, Eric W. "Primorial." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/Primorial.html
- Primorial, https://en.wikipedia.org/w/index.php?title=Primorial&oldid=1306197368 (dostęp: 2025-09-24).
- Weisstein, Eric W. "Primorial Prime." From MathWorld--A Wolfram Resource. https://mathworld.wolfram.com/PrimorialPrime.html
- Primorial in algorithms, Daniel Șuteu https://trizenx.blogspot.com/2020/01/primorials-in-algorithms.html
- Prime numbers and the (double) primorial sieve, Dicker H. https://primorial-sieve.com/_Primorial_sieve%20En.pdf