Tym razem ćwiczenia podsumowujące pierwszą część kursu. Przykłady dotyczą kilku zjawisk związanych z biologią, należy więc najpierw zrozumieć problem, a później przystąpić do projektowania i pisania programów. Biorąc pod uwagę, że wielu czytelników tej strony nie ma wiele spólnego z tą dziedziną, starałem się opisać zagadnienia w miarę przystępnie a stosowane modele i algorytmy są dość proste.
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Malthus, czyli wzrost wykładniczy i arytmetyczny
Thomas Malthus był angielskim ekonomistą i demografem, który pod koniec XVII wieku opublikował pracę An Essay on the Principle of Population. Zawarł w niej stwierdzenie, że populacja ludzka wzrasta w postępie geometrycznym, natomiast produkcja żywności w postępie arytmetycznym. Ponieważ wzrost arytmetyczny w dłuższej perspektywie nie nadąża za geometrycznym, wg. autora sytuacja taka prowadzi nieuchronnie do klęski głodu.
Opisany przez Malthusa związek miedzy populacją i żywnością nie jest oczywiście ograniczony do ludzi, ale może być podstawą stworzenia uproszczonego modelu wzrostu populacji z uwzględnieniem ograniczeń związanych z zasobami środowiska (niekoniecznie tylko żywności). Można w ten sposób na przykład symulować wzrost populacji zwierząt zasiedlających wyspę, czy rozwój bakterii w pożywce w warunkach laboratoryjnych. Tworząc odpowiedni model należy oczywiście wziąć pod uwagę specyfikę symulowanego układu: populacja – środowisko. Zauważ, że założenie o wzroście zasobów żywności, związane z postępem w rolnictwie, rzadko będzie miało rację bytu w modelach odnoszących się do warunków naturalnych czy rozwijających się w szalce drobnoustrojów.
Zadanie
Zadanie polega na stworzeniu symulacji wzrostu populacji i zasobów żywnościowych aż do chwili, kiedy zacznie brakować pożywienia. Zakładamy, że pokolenia nie zachodzą na siebie – liczebność populacji i ilość żywności zmienia się w kolejnych krokach co sezon. Program powinien na bieżąco wyświetlać zmieniające się parametry. Rozwiązania pojawią się mniej więcej za tydzień.
Założenia i wzory
Jak wynika z tezy Malthusa, będziemy mieli do czynienia z dwoma rodzajami wzrostu:
Wzrost arytmetyczny polega na tym, że ilość (żywności) wzrasta o pewną stałą wartość. Tak więc ilość pożywienia w kolejnym sezonie będzie równa:
Gdzie:
Z – ilość żywności, i – kolejny sezon, a – przyrost żywności w kolejnym sezonie (stała)
Z kolei dla wzrostu geometrycznego (wykładniczego) populacji obliczając jej wielkość w kolejnym sezonie mnożymy liczbę osobników w poprzednim sezonie o pewną stałą, która uwzględnia przyrost osobników (współczynnik wzrostu populacji):
Gdzie:
N – ilość osobników, i – kolejny sezon, R – współczynnik wzrostu populacji
Stała używana we wzorze może uwzględniać pojawienie się nowych osobników i śmierć części osobników populacji. Oczywiście, jeśli chcielibyśmy stworzyć bardziej realistyczny model należałoby uwzględnić takie czynniki jak dojrzewanie, przerwy między wydaniem kolejnych potomków, czy stosunek płci.
Ponieważ w programie uwzględniamy zależność między populacją a zasobami zakładamy, że każdy osobnik zużywa określoną ilość pożywienia (zasobów środowiska), którą możemy określić jakąś stałą (np. c). Populacja może się rozrastać dopóki ilość zużywanych zasobów nie przekracza zasobów istniejących w danym pokoleniu. Jeśli będzie większa, dochodzi do zatrzymania wzrostu o czym powinien poinformować program.
Wskazówki
Rozwiązanie - kliknij aby zobaczyć
Dobór naturalny
Tematem tego zadania będzie symulacja doboru naturalnego. Będziemy symulować ten proces na poziomie genetycznym a dokładniej jako zmiany częstości występowania dwu alleli jednego genu. Będziemy brali pod uwagę trzy genotypy: AA, Aa, oraz aa. Można sobie wyobrazić na przykład, że gen odpowiada za barwę sierści: czarną (genotyp AA), szarą (Aa) i białą (aa). Każdy z nich może mieć inne dostosowanie (w) co oznacza, że trzymając się powyższego przykładu barwa sierści ma wpływ na możliwość przetrwania albo zwabienie partnera. W skrócie – im większa wartość w tym więcej osobniki o danym genotypie pozostawiają przeciętnie potomstwa. Dostosowanie jest wartością wynikającą z porównania konkurujących osobników (genotypów), aby wartości miały biologiczny sens, powinny mieścić się między 0 a 1, przy czym, przynajmniej dla jednego z genotypów dostosowanie powinno wynosić 1.
W kolejnych pokoleniach dochodzi do losowego krzyżowania się osobników a to jak wiele powstaje osobników o poszczególnych genotypach wynika z tego jak dużo jest poszczególnych alleli w populacji a dokładnie z ich częstości, czyli frekwencji. Frekwencja danego allelu to liczba kopii tego allelu podzielona przez liczbę wszystkich alleli, może więc przyjmować wartości między 0 a 1.
Frekwencje alleli, oznaczane są zwyczajowo literami p (dla allelu A) i q (a).
Częstość genotypów (liczba osobników o danym genotypie/liczba wszystkich osobników), które powstają w kolejnym pokoleniu, można wyliczyć ze wzorów znanych z prawa Hardy’ego-Weinberga:
Gdzie: PAA – frekwencja genotypu AA, PAa – frekwencja genotypu Aa, Paa – frekwencja genotypu aa, p – frekwencja allelu A, q – frekwencja allelu a.
Pamiętaj, że suma frekwencji alleli jak też suma frekwencji wszystkich genotypów powinna wynosić 1.
Ponieważ, jak napisałem powyżej, genotypy mogą mieć różne wartości dostosowania a więc różna będzie ilość zostawionego przez nich potomstwa, będzie w takiej sytuacji mogło dochodzić do zmian częstości alleli w kolejnych pokoleniach. Zmianę częstości występowania allelu a opisuje wzór:
Gdzie: Δq – zmiana częstości allelu a (q) między pokoleniami, p – częstość allelu A, q -częstość allelu a, Ws – średnie dostosowanie, wAA – dostosowanie genotypu AA, wAa – dostosowanie genotypu Aa, waa – dostosowanie genotypu aa.
Z powyższych oznaczeń zagadkowe pozostaje średnie dostosowanie (Ws). Wyliczamy je ze wzoru:
Oznaczenia jak wyżej
Frekwencja allelu w kolejnym pokoleniu będzie więc wynosić:
Odpowiednie wartości dla drugiego allelu łatwo wyliczyć pamiętając, że p + q = 1.
W zależności od wprowadzonych wartości frekwencje alleli mogą się zachowywać w różny sposób. Może dojść do wyeliminowania jednego z alleli, albo frekwencja może zmierzać do tego stanu ale go nie osiągnąć, może też powstać stan równowagi.
Nieco szerzej temat jest omówiony w moich prezentacjach do kursu „Genetyka”, ale jeśli temat wydaje się interesujący to z pewnością warto zajrzeć do bardziej fachowej literatury.
Zadanie
Napisać program, który prześledzi zmiany frekwencji alleli i genotypów w populacji w kolejnych pokoleniach. Użytkownik podaje wartości dostosowania poszczególnych genotypów, początkowe frekwencje alleli oraz maksymalną liczbę pokoleń.
Program ma obliczać (i drukować na ekranie) te wartości oraz frekwencje genotypów w kolejnych pokoleniach aż frekwencja jednego z alleli osiągnie 1 (co oznacza, że drugi został wyeliminowany a więc dalsze zmiany nie są możliwe) lub liczba pokoleń osiągnie wartość dopuszczoną przez użytkownika lub wystąpi stan równowagi (brak zmian frekwencji alleli w kolejnych pokoleniach).
Działanie programu można skonfrontować np. z wynikami uzyskanymi w programie populus. Wybierz z menu: Model -> Quantative-Genetic Models: -> Population & Quantative Genetics, wpisz odpowiednie dane i porównaj wyniki.
Rozwiązanie - kliknij aby zobaczyć
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Nie zaglądałam do odpowiedzi, uparłam się, że zrobię sama :) I strasznie się cieszę, bo programy działają jak trzeba, a moje podejście okazało się zupełnie inne :)
Zad. 1.
package petle;
import java.util.Scanner;
public class WzorstPopulacji {
public static void main(String[] args) {
Scanner skaner = new Scanner(System.in);
System.out.println(„Podaj początkową wartość populacji: „);
int populacja = skaner.nextInt();
System.out.println(„Podaj początkową wartość zasobów żywieniowych [kg]: „);
long zasoby = skaner.nextLong();
System.out.println(„Podaj współczynnik wzrostu populacji: „);
int wzrostPopulacji = skaner.nextInt();
System.out.println(„Podaj przyrost ilości żywności w kolejnym sezonie [kg]: „);
long wzrostZasoby = skaner.nextLong();
System.out.println(„Podaj ilość zużywanych przez osobnika zasobów w sezonie [kg]: „);
int zuzywanie = skaner.nextInt();
System.out.println(„Sezon: 1 ” + „liczba osobnikow: ” + populacja + ” zasoby: ” + zasoby + ” kg” + ” nadmiar ” +
„zasobów: ” + (zasoby – (zuzywanie*populacja)) + ” kg”);
for (int i = 2; i < 100000000; i++){
long x = zasoby – (zuzywanie*populacja);
long y = populacja;
zasoby = (x) + wzrostZasoby;
populacja = populacja * wzrostPopulacji;
long oIleWzroslaPopulacja = populacja – y;
long nadmiarZasobow = zasoby – (zuzywanie*populacja);
if ((populacja*zuzywanie) < x) {
System.out.println("Sezon: " + i + " liczba osobników: " + populacja + " wzrost populacji o: " + oIleWzroslaPopulacja +
" zasoby: " + zasoby + " kg" + " nadmiar zasobów: " + nadmiarZasobow + " kg");
} else
break;
}
}
}
Zad. 2.
package petle;
import java.util.Scanner;
public class DoborNaturalny {
public static void main(String[] args) {
Scanner skaner = new Scanner(System.in);
System.out.println("Wpisz wartość dopasowania typu AA (wartość powinna mieścić się od 0 do 1): ");
double WAA = skaner.nextDouble();
System.out.println("Wpisz wartość dopasowania typu Aa (wartość powinna mieścić się od 0 do 1): ");
double WAa = skaner.nextDouble();
System.out.println("Wpisz wartość dopasowania typu aa (wartość powinna mieścić się od 0 do 1): ");
double Waa = skaner.nextDouble();
System.out.println("Podaj frekwencję allelu A (wartość powinna mieścić się od 0 do 1): ");
double p = skaner.nextDouble();
double q = 1-p;
System.out.println("Podaj liczbę pokoleń, przez które chcesz śledzić zmiany genotypu: ");
int pokolenia = skaner.nextInt();
System.out.println("Pokolenie: " + 1 + ", frekwencja allelu A: " + p + ", frekwencja allelu a: " + q + "," +
" frekwencja genutypu AA: " + (p*p) + ", frekwencja genotypu Aa: " + (p*q) + ", frekwencja genotypu " +
"aa: " + (q*q));
for(int i=2; i<=pokolenia; i++) {
double Ws = (p*p)*WAA + 2*p*q*Waa + (q*q)*Waa;
double zmianaQ = ((p*q)/Ws)*(q*(Waa-WAa)-p*(WAA-WAa));
double a = q;
q = a + zmianaQ;
p = 1 – q;
double PAA = p*p;
double PAa = p*q;
double Paa = q*q;
System.out.println("Pokolenie: " + i + ", frekwencja allelu A: " + p + ", frekwencja allelu a: " + q + "," +
" frekwencja genutypu AA: " + PAA + ", frekwencja genotypu Aa: " + PAa + ", frekwencja genotypu " +
"aa: " + Paa);
if ((p==1) || (q==1) || (p==q))
break;
}
}
}