matematykaszkolna.pl
Eliminacja Gaußa , pisanie kodu Mariusz: Filip Jeśli chodzi o eliminację Gaußa to strona o którą ci chodziło to ważniak http://wazniak.mimuw.edu.pl/index.php?title=MN05 Kiedyś znalazłem kod programu do odwracania macierzy i przełożyłem go z Pascala na C http://codepad.org/GZWexB6t Tutaj jako typ dla indeksów dałem unsigned int Przekładałem ten kod w miarę dokładnie i indeksowałem macierz od jedynki Jeśli chcesz zmodyfikować ten kod tak aby indeksować macierz od zera to zrób to ostrożnie aby nie spowodować problemów podczas wstecznej iteracji Zmienne sterujące instrukcjami iteracyjnymi proponowałbym zmienić indeksy podczas odwoływania się do elementów tablicy
8 sty 16:18
Filip: Cześć Mariusz, przez chwile byłem nieobecny, jednak wracam. Właśnie pierwszym krokiem do działającego programu rozwiązującego układy równań będzie napisanie przekształcenia macierzy ze współczynnikami przy niewiadomych do macierzy górnej trójkątnej. W linku który podałeś, chyba nie ma tego algorytmu, aby przejść do macierzy górnej trójkątnej, jest tylko rozkład LU, ale zakładam że jest to co innego Próbowałem tylko testowo narazie napisać przejście do macierzy górnej trojkątnej, tutaj wrzucam kod, jednak nie działa on poprawnie, gdzieś się wykrzacza #include <stdio.h> #include <stdlib.h> struct Matrix { int r, c; double** a; }; void schodkowanie(struct Matrix* m) { for (int k=0;k<m−>c−1;k++) { for (int w=k+1;w<m−>r;w++) { double wsp=m−>a[w][k]/m−>a[k][k]; for (int j=k;k<m−>c;j++) { m−>a[w][j]−=m−>a[k][j]*wsp; } } } } int main() { struct Matrix* m=malloc(sizeof(*m)); m−>r=4; m−>c=4; m−>a=malloc(m−>r*sizeof(*m−>a)); for (int i=0;i<m−>r;i++) { m−>a[i]=malloc(m−>c*sizeof(**m−>a)); } for (int i=0;i<4;i++) { for (int j=0;j<4;j++) fscanf(stdin, "%g", &m−>a[i][j]); } schodkowanie(m); for (int i=0;i<4;i++) { for (int j=0;j<4;j++) { printf("%lf ", m−>a[i][j]); } printf("\n"); } } Tutaj kolejne pytanie, jak zapobiec temu, ze m−>a[k][k] może być równe zero, co zrobić jeśli będzie?
8 sty 20:02
Filip: Blad znaleziony, w funkcji schodkowanie() w 3cim forze(), powinno byc j<m−>c, jednak nadal nie wiem czy dziala poprawnie
8 sty 20:06
Filip: Jeszcze jedno pytanie, mam równanie AX=B, modyfikuje tylko macierz A, czy powinienem modyfikować także macierz B w sprowadzaniu macierzy A do schodkowej?
8 sty 20:11
Mariusz: A dokonałeś częściowego wyboru elementu głównego ? W kolumnie poniżej głównej przekątnej wyszukujesz elementu największego co do wartości bezwzględnej i gdy go znajdziesz to zamieniasz wiersze Jeśli tym elementem głównym będzie zero to masz macierz osobliwą
8 sty 20:27
Mariusz: Kiedyś pisałem program do eliminacji Gaußa Tutaj ograniczyłem się do układów oznaczonych #include<stdio.h> #include<stdlib.h> #include<math.h> #include<conio.h> void gauss(double** Ab,int n,double *d) { int i,j,k,p; double temp,s; (*d) = 1.0; for( k = 0; k < n; k++) { p = k; for( j = k; j < n; j++) if( fabs(Ab[j][k]) > fabs(Ab[p][k]) ) p = j; for( j = 0; j <= n; j++) { temp = Ab[k][j]; Ab[k][j] = Ab[p][j]; Ab[p][j] = temp; } if(p != k) (*d) = −(*d); (*d) *= Ab[k][k]; if(Ab[k][k] == 0.0) return ; for( j = k + 1; j< n; j++) { s = (double)(Ab[j][k]/Ab[k][k]); for( i = k + 1; i <= n; i++ ) Ab[j][i] −= s * Ab[k][i]; } } } void backsub(double** Ab,int n) { int i,j; double sum; for(i = n − 1; i >=0 ;i−−) { sum = 0.0; for(j = i + 1; j < n;j++) sum += Ab[i][j]*Ab[j][n]; Ab[i][n] = (double)((Ab[i][n]−sum)/Ab[i][i]); } } int main() { int i,j,n; double d; double **A; char esc; do { printf("Rozwiazywanie ukladow rownan liniowych metoda eliminacji Gaussa\n"); printf("Podaj liczbe rownan \n"); scanf("%d",&n); A = (double**)malloc((n )*sizeof(double*)); for(i = 0;i < n;i++) A[i] = (double*)malloc((n+1)*sizeof(double)); for(i = 0;i < n; i++) { for(j = 0;j < n;j++) { printf("A[%d][%d]=",i,j); scanf("%lf",&A[i][j]); } printf("B[%d]=",i); scanf("%lf",&A[i][n]); } gauss(A,n,&d); for(i=0;i<n;i++) { for(j = 0;j<=n;j++) { printf("%lf ",A[i][j]); } printf("\n"); } if(d != 0) { backsub(A,n); for(i = 0;i < n;i++) printf("X[%d]=%.12lf\n",i,A[i][n]); } for(i = 0;i < n;i++) free(A[i]); free(A); esc = getch(); } while(esc != 27); }
8 sty 20:31
Mariusz: Ad 8 sty 2021 20:11 Modyfikujesz obydwie macierze
8 sty 20:33
Filip: To jeszcze przed przystąpieniem do pisania tego (faktycznie o tym zapomniałem) mam pytanie. Napisałeś "...W kolumnie poniżej głównej przekątnej..." Czy to zakłada, że elementu na głównej przekątnej nie bierzemy pod uwagę? W kodzie natomiast bierzesz ten element pod uwage
8 sty 20:43
Mariusz: Wydaje mi się że mógłbyś iterować bez tego elementu na głównej przekątnej
8 sty 21:13
Filip: Zmodyfikowałem moją funkcję, dodałem częściowy wybór elementu głównego: void schodkowanie(struct Matrix* m) { int q,s; for (int k=0;k<m−>c−1;k++) { s=0; for (int i=k;i<m−>r−2;i++) { if (abs(m−>a[i+1][k])>abs(m−>a[i+2][k])) { q=i+1; s=1; } } if (s==1) { for (int j=0;j<m−>r;j++) { double temp=m−>a[k][j]; m−>a[k][j]=m−>a[q][j]; m−>a[q][j]=m−>a[k][j]; } } if (m−>a[k][k]==0) return; for (int w=k+1;w<m−>r;w++) { double wsp=m−>a[w][k]/m−>a[k][k]; for (int j=k;j<m−>c;j++) { m−>a[w][j]−=m−>a[k][j]*wsp; } } } } Też właśnie mam się tylko ograniczyć do układów oznaczonych, natomiast jak zmodyfikować macierz wyrazów wolnych? Czy przeprowadzić taką samą operacje z odejmowaniem tak jak dla macierzy głównej? A, no i dodałem zmienną 's', która mi mówi, czy znaleziono w k−tej kolumnie pod diagonalą element główny, aby tylko zamieniać wiersze kiedy to konieczne − czy jest to dobry sposób?
8 sty 21:45
Filip: Diagonala to może złe określenie − przekątna
8 sty 21:47
Mariusz: Mam wątpliwości czy twój częściowy wybór elementu głównego będzie działał poprawnie
8 sty 21:57
Filip: Właśnie potestowałem i wygląda właśnie tak jak napisałeś, że nie działa poprawnie. Już doszedłem czemu. Ja porównuje elementy tylko obok siebie, przykładowo Kolumna pod główną przekątną 10 7 9 Moja część programu wskaże, że największy element (co do wartości bezwzględnej) to 9 (powinno być 10)
8 sty 22:02
Mariusz: Jak masz tablice jednowymiarową to jak wyszukujesz największy element ?
8 sty 22:31
Filip: Pierwszy element tablicy zapisujemy do zmiennej max. Kolejne elementy porównujemy z tą zmienną, jeśli natrafimy na wiekszy element, podmieniamy wartość zmiennej max. Zmieniłem już to w kodzie: maxsofar=abs(m−>a[k+1][k]); for (int i=k+2;i<m−>r−2;i++) { if (abs(m−>a[i][k])>maxsofar) { maxsofar=abs(m−>a[i][k]); s=1; q=i; } } Jednak ty znalazłeś chyba lepszy sposób z podmianą indeksu? Nie znam takiego
8 sty 23:41
Mariusz: No tak ja zapamiętywałem indeks największego elementu
9 sty 06:06
Mariusz: Podrzuć kod tego co już napisałeś tak abym mógł go przetestować
9 sty 08:25
Mariusz: Jeśli chodzi o odwracanie macierzy to zmodyfikowałem poprzedni kod tak aby tablice były indeksowane od zera Oszczędzi to trochę pamięć http://codepad.org/PJ1UfA0y
9 sty 11:48
Filip: https://pastebin.com/wncs4D90 Tutaj jest co do tej pory napisałem, kilka uwag: (1) funkcja schodkowanie() nie modyfikuje także macierzy wyrazów wolnych − musze to dodać (2) Program napisany aby sprawdzić czy działa schodkowanie − wprowadzanie danych nie jest wygodne, finalnie chce czytać dane z pliku tekstowego. (3) brakuje funkcji podstawienia wstecz − najprawdopodobniej dzisiaj ją dorobię Ogólnie program wymaga troche modyfikacji − najprawdopodobniej będe je dorabiał i na bieżąco podsyłał Pytanie: Czy fragment kodu, gdzie zamieniamy wiersze trzeba pisać iteracyjnie, czy można użyć swap()?
9 sty 14:08
Mariusz: A jest ona dostępna w C czy to dopiero wynalazek C++ ? Sprawdzę co tam do tej pory masz
9 sty 14:31
Mariusz: Ja tu chyba się źle wyraziłem Zmiennej w której przechowujesz maksa przed wejściem do pętli przypisujesz wartość elementu na głównej przekątnej a później już pętlę iterujesz od indeksu elementu znajdującego się zaraz poniżej głównej przekątnej i iterujesz już do końca kolumny Myślałem że chcesz zacząć iterować od elementu znajdującego się na głównej przekątnej tak jak ja (pętli nie musisz zaczynać od elementu na głównej przekątnej ale uwzględnić go chyba jednak musisz)
9 sty 14:46
Filip: Ja w pełni opuściłem element na głównej przekątnej, ponieważ moim maxsofar jest element ak+1k, w pętli iteruje od k+2 Przykładowo dla macierzy 1 2 3 4 4 5 6 4 7 8 9 4 1 2 3 4 weźmy kolumnę o indeksie 0 u mnie maxsofar=4 i zaczynam iterować i porównywać 7 i 4, 1 i 7 Masz racje, jeśli jednak element na głównej przekątnej trzeba także brać pod uwagę, w takim razie: maxsofar=akk i iteracje w pętli for() zaczynam od k+1 Ad. 14:31 Jednak tak, jest to wynalazek C++
9 sty 15:22
Mariusz: Ja poprawiłem już błędy w kodzie schodkowanie powinno już działać https://pastebin.com/index.php
9 sty 15:27
Mariusz: Wsteczne podstawianie możesz napisać w oddzielnej funkcji
9 sty 15:30
Mariusz: Dobrze by było aby efektem ubocznym działania funkcji schodkowanie było zwrócenie wyznacznika macierzy albo wskaźnikiem na liście parametrów albo instrukcją return
9 sty 15:34
Filip: Link który podesłałeś nie działa − w sensie dostaje pustą stronę. Co do wytycznych, które dostałem do pisania tej funkcji, to ma ona zwracać 1 gdy mamy macierz osobliwą, natomiast 0 gdy eliminacja została zakończona powodzeniem Jak podeślesz mi poprawiony kod, przeanalizuje go i postaram się to dopisać, ale to nie wszystko. Jeszcze musze zrobić nowy argument Matrix* b, gdzie to będzie macierz wyrazów wolnych i go także zmodyfikować odpowiednio w pętli I wtedy myślę, że ten etap będzie już zakończony i będzie można przejść do kolejnego
9 sty 15:48
Mariusz: Ach zapomniałem wysłać https://pastebin.com/fRpX9rqr Acha to ok jak ma takie wartości zwracać
9 sty 15:54
Mariusz: A to musisz wczytywać te macierze oddzielnie Na obydwu macierzach wykonujesz te same operacje więc można by to "włożyć" do jednej macierzy
9 sty 16:21
Filip: Tak, założyłem że czytam dane z dwóch plików tekstowych − jeden ze współczynnikami przy niewiadomych, drugi z wyrazami wolnymi, taki fragment kodu poglądowy: Matrix* A=readFromFile(argc>1?argv[1]:"A.txt"); //macierz współczynników Matrix* b=readFromFile(argc>2?argv[2]:"b.txt"); //macierzy wyrazów wolnych Matrix* x; //macierz z wynikami Zaraz podeśle dokończoną funkcję schodkowanie(Matrix* a, Matrix* b) dokończoną, zmienię jej nazwę na eliminate() (ze względu na to, że już przyjąłem anglojęzyczną formę nazywania zmiennych)
9 sty 17:10
Filip: int eliminate(struct Matrix* m, struct Matrix* b) { int q,s; double maxsofar; for (int k=0;k<m−>c;k++) { s=0; maxsofar=abs(m−>a[k][k]); for (int i=k+1;i<m−>r;i++) { if (abs(m−>a[i][k])>maxsofar) { maxsofar=abs(m−>a[i][k]); s=1; q=i; } } if (s==1) { for (int j=0;j<m−>r;j++) { double temp=m−>a[k][j]; m−>a[k][j]=m−>a[q][j]; m−>a[q][j]=temp; } } if (m−>a[k][k]==0) return 1; for (int w=k+1;w<m−>r;w++) { double wsp=m−>a[w][k]/m−>a[k][k]; for (int j=k;j<m−>c;j++) { m−>a[w][j]−=m−>a[k][j]*wsp; } b−>a[w][0]−=b−>a[k][0]*wsp; } } return 0; } Tak wygląda finalnie funkcja eliminate()
9 sty 17:31
Filip: Czy dobrze modyfikuje macierz wyrazów wolnych?
9 sty 17:39
Mariusz: Nie zamieniasz wierszy w tej macierzy Jeśli dopiszesz zamianę wierszy to schodkowanie powinno być ok
9 sty 18:29
Filip: int eliminate(struct Matrix* m, struct Matrix* b) { int q,s; double maxsofar; for (int k=0;k<m−>c;k++) { s=0; maxsofar=abs(m−>a[k][k]); for (int i=k+1;i<m−>r;i++) { if (abs(m−>a[i][k])>maxsofar) { maxsofar=abs(m−>a[i][k]); s=1; q=i; } } if (s==1) { for (int j=0;j<m−>r;j++) { double temp=m−>a[k][j]; m−>a[k][j]=m−>a[q][j]; m−>a[q][j]=temp; } double t=b−>a[k][0]; b−>a[k][0]=b−>a[q][0]; b−>a[q][0]=t; } if (m−>a[k][k]==0) return 1; for (int w=k+1;w<m−>r;w++) { double wsp=m−>a[w][k]/m−>a[k][k]; for (int j=k;j<m−>c;j++) { m−>a[w][j]−=m−>a[k][j]*wsp; } b−>a[w][0]−=b−>a[k][0]*wsp; } } return 0; } W takim razie tak to będzie wyglądać?
9 sty 19:07
Mariusz: Dopisz tę zamianę wierszy macierzy b i napisz funkcję do wstecznego podstawiania Na koniec piszesz to co ma być w funkcji main Czytanie z pliku i zapis do pliku zrealizujesz za pomocą oddzielnych funkcji które w funkcji main wywołasz ? Można i tak choć ja odczyt z pliku i zapis do pliku realizowałem już w funkcji main
9 sty 19:18
Mariusz: Teraz eliminacja wygląda ok Zostaje jeszcze wsteczne podstawianie Wsteczne podstawianie jest już łatwiejsze , możesz się wzorować na tym pseudokodzie z ważniaka
9 sty 19:28
Filip: Tak u mnie podstawienie w tył wygląda. jak widzisz funkcja zwraca wartość całkowitą, ponieważ tak mam w wytycznych (1) zwraca 2 − błąd nieprawidłowych rozmiarów macierzy −−−− tutaj nie wiem czy wszystko uwzględniłem w pierwszej linijce kodu w ciele funkcji (2) zwraca 1 − błąd dzielenia przez 0 (3) zwraca 0 − wsteczne podstawienie zakończone sukcesem int backsubst(struct Matrix* m, struct Matrix* x, struct Matrix* b) { if (x−>c!=1||x−>r!=m−>r||m−>r!=m−>c||b−>c!=1||b−>r!=m−>r) return 2; for (int i=m−>r−1;i>=0;i−−) { double sum=0.0; for (int j=i+1;j<m−>r;j++) { sum+=m−>a[i][j]*x−>a[j][0]; } if (m−>a[i][i]==0) return 1; x−>a[i][0]=(b−>a[i][0]−sum)/m−>a[i][i]; } return 0; }
9 sty 20:20
Filip: Natrfaiłem na problem, gdy odwołuję się do macierzy wyrazów wolnych. odwołouje sie do niej za pomocą b−>a[i][0] czy powinienem b−>a[i]? Program gdzieś się wykrzacza
9 sty 20:36
Mariusz: Teraz trzeba by przetestować tą funkcję realizującą podstawianie w tył w przypadku źle podanych danych bo dla poprawnych danych zdaję się działać ok A jeszcze jedno do obliczania wartości bezwzględnej lepiej użyć funkcji fabs
9 sty 20:47
Mariusz: U mnie dla poprawnych danych działa ok Nie testowałem jeszcze dla błędnych danych Myślę że odwołanie do tablicy jest poprawne b−>a[i][0]
9 sty 20:57
Filip: Tak, tez teraz przetestowałem na kilkunastu układach równań i wygląda na to, że pierwiastki dobrze oblicza
9 sty 21:08
Mariusz: Wiesz co jeszcze mógłbyś dodać − globalną stałą (czy makrodefinicję) która udawałaby zero Przydałaby się ze względów numerycznych w sytuacji gdybyś musiał dzielić przez bardzo małą liczbę Lepiej jest wtedy udać że macierz jest osobliwa
9 sty 21:50
Filip: Hmm, tego chyba nie robiłem nigdy, co to znaczy że by udawała 0, byłaby zerem czy nie do końca?
9 sty 22:11
Mariusz: Chodzi o to że ta stała byłaby liczbą bardzo bliską zera Gdybyś po wyszukaniu największego elementu w kolumnie miał bardzo małą liczbę to mógłbyś udać że jest to macierz osobliwa Udać bo z matematycznego punktu widzenia macierz nie byłaby osobliwa jednak ze względów numerycznych warto uznać wtedy że mamy wtedy macierz osobliwą Ba gdybyś miał dostatecznie małą liczbę to podczas dzielenia mógłbyś wyjść poza zakres obranego typu zmiennych
9 sty 22:23
Filip: Mariusz odbiegając trochę od tematu... widziałem, że liczysz całki przez podstawienie Eulera, czy masz jakieś przykłady na jego zastosowanie? Właśnie zobaczyłem, że jednak miałem ten sposób i chciałbym sobie przećwiczyć
10 sty 02:02
Mariusz: Mogę ci dać jakieś ale gdybym chciał ci je tutaj sprawdzić to kerajsy będą się wtrącać i przeszkadzać Tutaj kerajs nie zna się na programowaniu i się nie wtrącał i od razu przyjemniej się pisało Jeśli chodzi o podstawienia Eulera to Z tego co zauważyłem to jak masz całkę
 1 

dx
 xnax2+bx+c 
to najpierw patrzysz czy możesz użyć drugiego podstawienia ax2+bx+c=xt±c przy czym znak + lub − możesz sam wybrać Jeśli nie to sprawdzasz czy możesz użyć pierwszego podstawienia ax2+bx+c=t±ax a na końcu dopiero sprawdzasz trzecie podstawienie Eulera ax2+bx+c=(x−x1)t Jak masz całkę z
 W(x) 

dx
 ax2+bx+c 
to sprawdzasz czy możesz zastosować pierwsze podstawienie Jeśli nie to możesz oczywiście sprawdzać pozostałe podstawienia ale można też się zastanowić nad innymi sposobami liczenia całek takiej postaci
 1 

dx
 xx2+4x−4 
Pierwsze podstawienie będzie najwygodniejsze w użyciu
 1 

dx
 xx2+x+1 
Drugie podstawienie będzie najwygodniejsze w użyciu
 1 

dx
 (x+2)4−x2 
Trzecie podstawienie będzie najwygodniejsze w użyciu A teraz całka którą kiedyś dostałem z równania różniczkowego
 1 

dx
 x2(4x2−3)2x2−1 
Tutaj pierwsze podstawienie będzie wygodniejsze o ile po tym podstawieniu zauważysz coś w mianowniku Policz te co wymieniłem a później zobaczymy
10 sty 05:37
Mariusz: A jeszcze jedno co do kodu to tak z ciekawości zapytam czemu nie zwalniasz pamięci
10 sty 07:02
Filip: zapomniałem o zwalnianiu pamięci
10 sty 12:05
Mariusz: Filip to zadanie miałeś na metodach numerycznych czy np na programowaniu w C Jak na metodach numerycznych to są też iteracyjne metody rozwiązywania układów równań liniowych np iteracja Jacobiego czy Gaußa−Seidela
10 sty 16:59
Filip: na programowaniu w C
10 sty 21:19
Mariusz : Teraz ćwiczysz metodę Ostrogradskiego i skoro uczysz się programować to mógłbyś też oprogramować działania na wielomianach Strukturka do reprezentowania wielomianu mogłaby wyglądać tak struct Polynomial { int deg; double *coeff; }; a co do operacji na wielomianach to przydałyby się np takie dodawanie wielomianów odejmowanie wielomianów mnożenie wielomianów iloraz z dzielenia dwóch wielomianów reszta z dzielenia dwóch wielomianów NWD dwóch wielomianów no i opcjonalnie jakieś numeryczne metody szukania pierwiastków tych wielomianów
11 sty 12:19
Filip: Ok, zakładam, że: deg − stopień wielomianu coeff − vector ze współczynnikami przy kolejnych potęgach przy x Przykładowo (czy dobrze rozumiem), zapiszmy w kodzie taki wielomian: 3x5−12x4+9x2−0.5x+18 W kodzie: struct Polynomial* p = malloc(sizeof(*p)); p−>deg = 5; p−>coeff = malloc(sizeof(*coeff) + 1) // dodajemy miejsce dla x0 czyli wyraz wolny p[0] = 18 p[1] = −0.5 p[2] = 9 p[3] = 0 p[4] = −12 p[5] = 3 czyli p[i] reprezentuje kolejne współczynniki przy xi Jeżeli dobrze zrozumiałem jak to wygląda w kodzie, mogę przejść do pisania kolejnych funkcji
11 sty 12:52
Mariusz : No tak to miałoby wyglądać
11 sty 12:53
Filip: To możemy zacząć po kolei, czyli od dodawania, napisałbym to tak: struct Polynomial* addPolynomials(struct Polynomial* a, struct Polynomial* b) { int newDeg = a−>deg > b−>deg ? a−>deg : b−>deg; struct Polynomial* new = malloc(sizeof(*new)); new−>coeff = malloc(sizeof(*coeff) + 1); for (int i = newDeg − abs(a−>deg − b−>deg); i >= 0; i++) { new−>coeff[i] = a−>coeff[i] + b−>coeff[i]; } if (a−>deg > b−>deg) { for (int i = a−>deg; i >= a−>deg − abs(a−>deg − b−>deg); i−−) { new−>coeff[i] = a−>coeff[i]; } } else { for (int i = b−>deg; i >= b−>deg − abs(a−>deg − b−>deg); i−−) { new−>coeff[i] = b−>coeff[i]; } } //szukanie nowego stopnia wielomianu wyjściowego for (int i = newDeg; i >= 0; i++) { if (new−>coeff[i] != 0) { new−>deg = coeff[i]; return new; } new−>deg = 0; return new; } Takie coś udało mi się skleić na "sucho", nie testowałem czy działa bo pisałem od razu tutaj, idea jest prosta Mamy załóżmy dwa wielomiany W(x) = x4 + 3x2 + x − 1 −−−−> deg = 4 Q(x) = x2 − 2x + 1 −−−−> deg = 2 Orazy wyjściowy P(x) −−−> deg 4 (zakładamy, bo może być sytuacja że współczynnik będzie wynosić 0) Reprezentując je tablicowo: W(x) − {−1, 1, 3, 0, 1} Q(x) − {1, −2, 1} P(x) − { , , , , ,} newDeg = 4 Pierwsza pętla for(), od i = 2 do 0 włącznie P(x) = {0, −1, 4, , } − stan po wykonaniu całej pętli 3 razy Pózniej musimy uzupełnić współczynnik z wielomianu o większym stopniu Po ifie() P(x) = {0, −1, 4, 0, 1} Matematycznie: W(x) + Q(x) = x4 + 3x2 + x − 1 + x2 − 2x + 1 = x4 + 4x2 − x
11 sty 13:12
Filip: W if() else w warunku pętli for() (w obu przypadkach) zamiast znaku >= powinno być >
11 sty 13:14
Mariusz : Gdzieś segmentation fault dostajesz
11 sty 14:16
Filip: Tak, w pierwszej pętli for() zamiast i++ powinno być i−−, teraz dopiero zauważyłem jak powiedziałeś
11 sty 14:22
Filip: Zmodyfikowałem funkcję, działała źle, ponieważ niepoprawnie alokowałem pamięć: struct Polynomial* addPolynomials(struct Polynomial* a, struct Polynomial* b) { int newDeg = a−>deg > b−>deg ? a−>deg : b−>deg; struct Polynomial* n = malloc(sizeof(*n)); n−>coeff = malloc((newDeg + 1) * sizeof(*n−>coeff)); for (int i = newDeg − abs(a−>deg − b−>deg); i >= 0; i−−) { n−>coeff[i] = a−>coeff[i] + b−>coeff[i]; printf("%lf ", n−>coeff[i]); } if (a−>deg > b−>deg) { for (int i = a−>deg; i > a−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = a−>coeff[i]; } } else { for (int i = b−>deg; i > b−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = b−>coeff[i]; } } for (int i = newDeg; i >= 0; i++) { if (n−>coeff[i] != 0) { n−>deg = i; return n; } } n−>deg = 0; return n; }
11 sty 15:36
Filip: A, no i w ostatniej pętli for zamiast i++, i−−
11 sty 15:38
Filip: Okej, potestowałem i wygląda na to, że działa poprawnie, z odejmowaniem to po prostu zamieniam operator + na −?
11 sty 15:41
Mariusz : No u mnie taki kod działa jeśli chodzi o dodawanie https://pastebin.com/8h0sWZs8 No możesz od tej zamiany operatora zacząć i sprawdzimy czy działa poprawnie
11 sty 15:49
Filip: Dokładnie, kod taki sam, tylko nazwy zmiennej się różnią, ja zmieniłem z new na n, ponieważ w moim edytorze mi new podświetlało − a tego nie chciałem. Co do odejmowania, zmiana operatora to jednak nie wszystko. Po testowaniu, doszedłem do wniosku, że gdy dodajemy współczynniki, które nie brały udziału w odejmowaniu, powinniśmy zmienić ich znak na przeciwny czyli ten fragment kodu zmienić także if (a−>deg > b−>deg) { for (int i = a−>deg; i > a−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = a−>coeff[i]; } } else { for (int i = b−>deg; i > b−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = b−>coeff[i]; } }
11 sty 15:57
Filip: Hmm, wiesz co, chyba zbyt szybko do tego doszedłem, wydaje się to być bardziej skomplikowane, narazie nie wiem czy ta powyższa zmiana wystarczy
11 sty 16:05
Filip: ehh, sorry że tak cały czas pisze emotka, powyższa zmiana jest prawie dobra, powinno być if (a−>deg > b−>deg) { for (int i = a−>deg; i > a−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = a−>coeff[i]; } } else { for (int i = b−>deg; i > b−>deg − abs(a−>deg − b−>deg); i−−) { n−>coeff[i] = b−>coeff[i]; } } Jak dla mnie to wystarczy plus oczywiście zmiana operatora wcześniej
11 sty 16:13
Mariusz : Jeśli chodzi o mnożenie to naiwny algorytm ma złożoność O(n2) Mnożenie można przyśpieszyć korzystając z szybkiej transformaty Fouriera
11 sty 17:14
Mariusz : Jeśli chodzi o dzielenie to w książce Numerical Recipes in C jest funkcja https://www.cec.uchile.cl/cinetica/pcordero/MC_libros/NumericalRecipesinC.pdf
11 sty 18:03
Filip: Dokładnie, napisałem mnożenie tak jak mówiłeś, czy złożoność tego to nie będzie O((a−>deg+1)* (b−>deg+1))? struct Polynomial* multiplyPolynomials(struct Polynomial* a, struct Polynomial* b) { struct Polynomial* n = malloc(sizeof(*n)); n−>deg = a−>deg + b−>deg; n−>coeff = calloc((a−>deg + b−>deg + 1), sizeof(*n−>coeff)); for (int i = 0; i < a−>deg + 1; i++) { for (int j = 0; j < b−>deg + 1; j++) { n−>coeff[i + j] += a−>coeff[i] * b−>coeff[j]; } } return n; }
11 sty 18:58
Mariusz : No tak miałem na myśli przypadek dla wielomianów tego samego stopnia bo wtedy będziemy mieli taką złożoność
11 sty 19:19
Mariusz : Dopisałem funkcję wypisującą wielomian void printPolynomial(struct Polynomial *c) { for(int i = c−>deg;i >=0;i−−) if(c−>coeff[i] < 0.0) printf("−%lf x ** %d",−c−>coeff[i],i); else if(c−>coeff[i] > 0.0) printf("+%lf x ** %d",c−>coeff[i],i); printf("\n"); } Dałem podwójny asterisk jako operator potęgowania bo tamtego znaku nie wyświetla tutaj poprawnie
11 sty 19:26
Filip: Ok, właśnie ją skopiowałem do mojego programu, będzie wygodniej wyświetlać i czytać otrzymany wielomian. Jak rozumiem, nie da się zrobić, by zamiast x**5 wyświetlało w konsoli przykładowo x5
11 sty 19:45
Filip: Właśnie czytam o dzieleniu wielomianów i zdania są podzielone. Z jednej strony schemat Hornera będzie tylko dobry do dzielenie wielomianu przez dwumian (x−a), bo podobno ciężko go napisać aby działał dla większych wielomianów. Sugerują aby wzorować się na metodzie pisemnej. Na ważniaku znalazłem algorytm http://wazniak.mimuw.edu.pl/index.php?title=Zaawansowane_algorytmy_i_struktury_danych/Wyk%C5%82ad_4#Dzielenie_wielomian.C3.B3w Gdzie w książce, która mi podesłałeś jest dzielenie? Nie mogę znaleźć
11 sty 20:20
Mariusz : Na stronie 175 czyli w polu gdzie masz numery stron wpisz liczbę 199
11 sty 21:17
Mariusz : Schemat Hornera też się może przydać np gdybyśmy chcieli policzyć wartość wielomianu dla jakiejś liczby
11 sty 21:19
Filip: Właśnie, można dodać funkcję, która liczy wartość wielomianu dla dowolnej wartości. Pierwsze co przychodzi na myśl, to po prostu popodnosić nasze x do potęgi i−tej itp, jednak czy takie rozwiązanie nie będzie optymalne? Można wykorzystać funkcję pow(), która chyba jest już dostępna w C
11 sty 21:24
Mariusz : Tak funkcja pow jest dostępna w C ale jeśli chcesz mieć optymalne rozwiązanie to raczej korzystasz ze schematu Hornera Jeśli chodzi o dzielenie to w wyniku dostajesz dwa wielomiany więc jeśli chcesz je napisać w jednej funkcji to np iloraz zwracasz instrukcją return a resztę przekazujesz na liście paqrametrów
11 sty 21:44
Filip: Zaimplementowałem dzielenie do mojego programu, wygląda na to, że działa zarówno jak i dla dzielenie z resztą jak i bez reszty. Kod: struct Polynomial* dividePolynomials(struct Polynomial* a, struct Polynomial* b, struct Polynomial* r) { struct Polynomial* q = malloc(sizeof(*q)); q−>coeff = malloc((a−>deg + 1) * sizeof(*q−>coeff)); q−>deg = a−>deg; for (int i = 0; i < a−>deg + 1; i++) { r−>coeff[i] = a−>coeff[i]; q−>coeff[i] = 0.0; } for (int i = a−>deg − b−>deg; i >= 0; i−−) { q−>coeff[i] = r−>coeff[b−>deg + i] / b−>coeff[b−>deg]; for (int j = b−>deg + i − 1; j >= i; j−−) { r−>coeff[j] −= q−>coeff[i] * b−>coeff[j − i]; } } for (int i = b−>deg; i < a−>deg + 1; i++) { r−>coeff[i] = 0.0; } return q; }
11 sty 22:46
Filip: Daj znać, jakbyś zaalokowałam pamięc dla r−>coeff oraz q−>coeff, ja tutaj zaalakowałem, zakładając te same stopnie co dla wielomianu, którego dzielimy, jednak czy nie dałoby się zrobić, że: (1) q−>coeff to współczynniki nowego wielomiany, więc największa potęga przy x może wynosić a−>deg − b−>deg (2) r−>coeff można zaalokować jako b−>deg − 1
11 sty 22:53
Filip: Właśnie tak jak napisałem to zrobiłem na początku, jednak mi wychodziło poza tablice, ponieważ pierwszy for() działą w zakresie 0 − a−>deg, tego się chyba nie da obejśc, więc r−>coeff oraz q−>coeff muszą być takich samych rozmiarów jak a−>coeff. Ewentualnie można zmienić q−>deg i r−>deg, jednak matematycznie nie pamiętam jak to jest z tymi wykładnikami potęg w wielomianach
11 sty 23:20
Mariusz: Jeśli chodzi o stopnie tych wielomianów to co gdyby stopień dzielnika był większy niż stopień dzielnej ? Co wyszłoby z różnicy tych stopni ? Czy wtedy stopień reszty jest ostro mniejszy od stopnia dzielnika ? Gdybyś przyjął że stopień reszty jest równy stopniu dzielnika to z ostatniej pętli for powinieneś zrezygnować No właśnie gdybyśmy chcieli zoptymalizować kod to tą pierwszą pętlę for też trzeba by było zmodyfikować , być może dodać w niej instrukcję warunkową if albo rozbić na dwie pętle Problemem może być to że ta zagnieżdżona pętla jest skonstruowana w oparciu o założenie że r−>deg = a−>deg i to tutaj może wystąpić to "wyjeżdżanie" indeksem poza tablicę
12 sty 06:23
Mariusz: Na pierwszy rzut oka widać że algorytm którego użyli potrzebuje dodatkowej pamięci do przeprowadzenia obliczeń i właśnie te pomocnicze obliczenia zapamiętuje w zmiennej r dla której zaalokował więcej pamięci niż potrzeba Jeśli chodzi o te stopnie to stopień dla q da się ustalić bez większego problemu Jeśli chodzi Można by pomyśleć nad takimi rozwiązaniami 1. Wielomian r stopnia a−>deg można rozbić na dwa wielomiany z czego jeden będzie zmienną lokalną a drugi będzie wyprowadzany na liście parametrów funkcji jako reszta 2. Spróbować modyfikować wielomian a
12 sty 07:47
jc: Wtrącę się. Mariusz, masz rację, wartość wielomianu liczymy wg wzoru: ax3+bx2+cx+d = ((ax+b)x+c)x+d W przypadku wielomianów przekazujemy wskaźniki (no, chyba że masz wielomiany niskiego, ustalonego stopnia).
12 sty 10:04
Mariusz: jc a ty co byś odpowiedział ba pytanie Filipa zadane we wpisie z 11 sty 2021 22:53
12 sty 12:20
Filip: Aby uniknąć przykładowo dzielenia wielomianu o niższym stopniu niż ten przez którego dzielimy, można na początku w ciele funkcji po prostu dodać if (a−>deg < b−>deg) .... No właśnie nie przyjmuje się tak, że stopień reszty jest mniejszy niż stopień dzielnika (?)
12 sty 12:33
Mariusz: No tak się przyjmuje if (a−>deg < b−>deg) { q = 0; r = a; } Gdybyś chciał napisać NWD wielomianów to przydałaby się jeszcze funkcja sprawdzająca czy wielomiany są równe no i korzystałbyś już z wcześniej napisanych funkcji zwłaszcza tej która realizuje dzielenie wielomianów Pętla while byłaby wygodniejsza w użyci oraz pewnie przyda się jakaś zmienna lokalna reprezentująca pomocniczy wielomian
12 sty 13:07
Filip: Jak takie NWD(P(x), Q(x)) znaleźć Jak rozumiem pierw sprawdzamy czy p−>deg > q−>deg − jeśli tak − idziemy dalej
 P(x) 
(1) Wykonujemy działanie

− jeżeli reszta wynosi 0 to NWD(P(x), Q(x)) = Q(x)
 Q(x) 
(2)w przeciwnym razie przypisujemy P(x) wartość Q(x), a Q(x) wartość otrzymanej reszty i powracamy do kroku (1) (?) Wezmę jakiś przykład, mamy wielomiany P(x) = x3+x2−3x−3 Q(x)=x2+3x+2
 P(x) 
(1)

 Q(x) 
P(x)=(x−2)Q(x)+(x−1) − reszta != 0 (2) P(x)=Q(x) Q(x)=(x+1) Wracamy do kroku 1
P(x) 

=(x+2)(x+1) + 0 −− reszta 0, czyli NWD(P(x), Q(x)) = x+1
(x+1) 
Czy to będzie coś tak wyglądać mniej więcej?
12 sty 13:57
Mariusz: No mniej więcej tak to mogłoby wyglądać
12 sty 14:08
Filip: Może do tego przydałaby się funkcja, która sprawdza, które stopnia jest wielomian − jeśli byłby zerowy, np W(x)−>reg = −1
12 sty 15:10
Mariusz: Myślałem o takiej funkcji int equals(struct Polynomial *a,struct Polynomial *b) { int result = (a−>deg == b−>deg); for(int i=a−>deg;result && i >=0 ; i−−) result = result && (a−>coeff[i] == b−>coeff[i]); return result; } i porównywać z wielomianem zerowym ale może twój pomysł jest lepszy Jeśli chodzi o stopień wielomianu p to czy nie wystarczy odwołać się do pola p−>deg;
12 sty 15:30
Filip: ja myślałem aby napisać to tak: − raczej będzie to szukać stopnia wielomianu, bo przy NWD() dostaniemy wielomian, jednak nie wiemy jakiego stopnia int checkDeg(struct Polynomial* p) { for (int i = p−>deg; i >= 0; i−−) { if (p−>coeff[i] != 0.0) { return −1; } return 1; } Ja myslałem by coś takiego zrobić, gdyż nasza reszta zakładamy że będzie tak samo jak a−>deg Tutaj w tej funkcji sprawdzimy, jakiego stopnia rzeczywiście jest nasza reszta − funkcja zwraca −1 gdy reszta nie jest stopnia zerowego, natomiast 1 jeśli jest Jednak ta funkcja sprawdza tylko czy wielomian jest zerowy trzebaby zmienić jej nazwę jednak nie wiem jak w języku angielskim jest wielomian zerowy Jednak nie wiem czy ta funkcja ma sens, jeśli tak − przdałaby się funkcja która reallocuje pamięć dla wielomianu. Przykładowo, zakładamy, że wielomian będzie maksymalnie stopnia 5 (przykładowo założyliśmy to przy dzieleniu) jednak w tablicy może wyglądać tak: [a, b, 0, 0, 0, 0] Po wrzuceniu do funkcji checkDeg() wyjdzie, że wielomian jest stopnia pierwszego, to w sumie już można reallocować w środku tej funkcji Zrozumiałeś o co mi chodzi?
12 sty 16:56
Mariusz: To może tak int checkDeg(struct Polynomial *p) { for(int i = p−>deg; i >= 0; i−−) { if(p−>coeff[i] != 0.0) return i; } return −1; } A co do realokacji pamięci to powinna być dostępna w standardzie funkcja realloc()
12 sty 17:42
Filip: Coś siadłem do NWD, coś takiego udało mi się zrobić − taki ala szablon można powiedzieć struct Polynomial* NWD(struct Polynomial* a, struct Polynomial* b) { struct Polynomial* temp = malloc(sizeof(*temp)); temp−>deg = b−>deg; temp−>coeff = malloc((temp−>deg + 1) * sizeof(*temp−>coeff)); while (checkDeg(b) != −1) { //tutaj by trzeva zrobić, aby wielomian a przejęło deg i *coeff po b temp = b; dividePolynomials(a, b, b); a = temp; } return temp; } Daj znać czy jest chociaż trochę poprawne
12 sty 19:10
Mariusz: Niedawno w C# zacząłem pisać klasę liczb wymiernych i tam NWD przydaje się do skracania ułamków Dla liczb NWD mogłoby tak wyglądać private static long Gcd(long a,long b) { long c; a = Math.Abs(a); b = Math.Abs(b); //Przy wielomianach dwie powyższe instrukcje pomijasz while(b != 0) { c = a; a = b; b = c % b; } return a; } I tutaj trzeba by napisać coś podobnego Myślę że idziesz w dobrym kierunku Lepiej by było usprawnić tą funkcję realizującą dzielenie ale jak nie mamy na to pomysłu
12 sty 20:09
Mariusz: Może dobrze by było gdyby tak przepisać funkcję realizującą dzielenie tak aby nie modyfikować wielomianów a oraz b np struct Polynomial* dividePolynomials(struct Polynomial* a, struct Polynomial* b, struct Polynomial* r) { struct Polynomial* q = malloc(sizeof(*q)); struct Polynomial* temp = malloc(sizeof(*temp)); temp−>coeff = malloc((a−>deg + 1) * sizeof(*temp−>coeff)); temp−>deg = a−>deg; q−>coeff = malloc((a−>deg + 1) * sizeof(*q−>coeff)); q−>deg = a−>deg; for (int i = 0; i < a−>deg + 1; i++) { temp−>coeff[i] = a−>coeff[i]; q−>coeff[i] = 0.0; } for (int i = a−>deg − b−>deg; i >= 0; i−−) { q−>coeff[i] = temp−>coeff[b−>deg + i] / b−>coeff[b−>deg]; for (int j = b−>deg + i − 1; j >= i; j−−) { temp−>coeff[j] −= q−>coeff[i] * b−>coeff[j − i]; } } for(int i = 0;i < b−>deg;i++) r−>coeff[i] = temp−>coeff[i]; return q; }
12 sty 21:21
Filip: Wydaje mi się, że już początkowo ta funkcje nie modyfikowała wielomianów a i b, tylko r. Co do NWD() to właśnie się wzorowałem na funkcji dla liczb całkowitych, jednak jest to zoptymalizowana wersja. Zapewne znasz wersje z odejmowaniem − czy zadziałałoby to na naszych wielomianach? Jutro postaram się poprawić NWD()
12 sty 22:23
Mariusz: Co do tej wersji z odejmowaniem to nie jestem pewny czy zadziała dla wielomianów Spróbuj pomyśleć nad wersją z dzieleniem a testowałeś tę funkcję realizującą dzielenie np na przykładach takich jak dividePolynomials(a,b,b);
12 sty 22:29
Filip: Nie, ale teraz podejrzewam, że może być to problematyczne z takim kodem, iż na nowo alokujemy zaalokowana pamięć − czy jest to dozwolone, może powinniśmy w funkcji dzielącej sprawdzać co jest zaalokowane dla wielowianu r a co nie
12 sty 22:41
Mariusz: No nie wiem w kodzie z 12 sty 2021 21:21 przydałoby się przed zwróceniem wartości zwolnić pamięć na zmienną temp Ja sprawdziłem funkcję dzielącą z parametrami a,b,b i działała ok (oczywiście ta wersja z dodatkową zmienną )
12 sty 23:31
Filip: No właśnie nie rozumiem jaka idea jest dodawanie kolejnej zmiennej? Mógłbyś przybliżyć, bo aktualnie nie widzę, w czym jest różnica czy będziemy pierw działać na "surowym" r niż na temp i później kopiować elementy z temp do q
12 sty 23:40
Mariusz: Zauważ że w kodzie modyfikujemy r i gdy r będzie tym samym co b to możemy dostać błędne obliczenia Gdy wywołujemy funkcje np z parametrami a,b,r to wersja będzie bez dodatkowej zmiennej ale jeśli wywołamy funkcje z parametrami a, b, b to r będzie tym samym co b i możemy otrzymać błędny wynik
13 sty 00:16
Filip: aa racja, czyli w takim razie trzeba na końcu funkcji dodać zwalnianie pamięci, przykładowo tak: free(temp−>coeff) free(temp) czyli po wywołaniu funkcji z argumentami takimi dividePolynomials(a, b, b) − w wielomianie b
 a 
powinniśmy mieć resztę z dzielenia

 b 
13 sty 09:42
Filip: Tak sobie teraz pomyślałem, że można dodać funkcję rest, która zwróci nam reszta z dzielenia struct Polynomial* Rest(struct Polynomial (*)diviedPolynomials(struct Polynomial*, struct Polynomial*, struct Polynomial*), struct Polynomial* a, struct Polynomial* b, struct Polynomial* r) Taki szablon funkcji − jednak nie wiem czy to już nie będzie zbyt duże kombinowanie
13 sty 09:52
Filip: Ten argument ostatni (r) jest niepotrzebny
13 sty 09:54
Filip: Zmodyfikowałem trochę twój kod z dzieleniem, bo początkowo nie działało mi dla argumentów (a, b, b) − nie wiem czy u ciebie też U mnie problem był taki, że gdy r−>deg < a−>deg to wyjeżdżało poza tablicę, dodałem ifa(), aby temu zapobiec + na początku warunek początkowy także dodałem Teraz wygląda na to, że działa poprawnie, kod: struct Polynomial* dividePolynomials(struct Polynomial* a, struct Polynomial* b, struct Polynomial* r) { if (a−>deg < b−>deg) return NULL; struct Polynomial* q = malloc(sizeof(*q)); struct Polynomial* temp = malloc(sizeof(*temp)); temp−>coeff = malloc((a−>deg + 1) * sizeof(*temp−>coeff)); temp−>deg = a−>deg; q−>coeff = malloc((a−>deg + 1) * sizeof(*q−>coeff)); q−>deg = a−>deg; for (int i = 0; i < a−>deg + 1; i++) { temp−>coeff[i] = a−>coeff[i]; q−>coeff[i] = 0.0; } for (int i = a−>deg − b−>deg; i >= 0; i−−) { q−>coeff[i] = temp−>coeff[b−>deg + i] / b−>coeff[b−>deg]; for (int j = b−>deg + i − 1; j >= i; j−−) { temp−>coeff[j] −= q−>coeff[i] * b−>coeff[j − i]; } } for (int i = 0; i < b−>deg; i++) { r−>coeff[i] = temp−>coeff[i]; } if (r−>deg < a−>deg) { for (int i = b−>deg; i < r−>deg + 1; i++) { r−>coeff[i] = 0.0; } } else { for (int i = b−>deg; i < a−>deg + 1; i++) { r−>coeff[i] = 0.0; } } free(temp−>coeff); free(temp); return q; }
13 sty 10:27
Filip: A, no i zrobiłem test, jakbyś kwestionował ten fragment kodu − u ciebie go nie widzę: if (r−>deg < a−>deg) { for (int i = b−>deg; i < r−>deg + 1; i++) { r−>coeff[i] = 0.0; } } else { for (int i = b−>deg; i < a−>deg + 1; i++) { r−>coeff[i] = 0.0; } } Jak dla mnie jest on potrzebny, zrobiłem mały test: Dzieliłem wielomiany: x4−3x3+3x2−4x+3 przez x−1 Z tym fragmentem kodu w funkcji dzielącej otrzymuję resztę 0 − czyli taka jaka powinna wyjść, natomiast bez tego fragmentu otrzymuję resze 1, więc wydaje mi się, że musi być
13 sty 10:35
Mariusz: W przypadku gdy if (a−>deg < b−>deg) dobrze by było do reszty przekopiować wielomian a natomiast zwrócić wielomian zerowy Tak po napisaniu to zauważyłem i poprawiłem to u siebie
13 sty 10:57
Mariusz: Mógłbyś się jeszcze pobawić tą funkcją dzielącą tak aby wielomian wyprowadzany poza funkcję jako reszta miał dokładnie taki stopień jaki powinien mieć wtedy funkcja checkDeg nie będzie potrzebna
13 sty 11:09
Filip: Dopisałem taką funkcję void findDeg(struct Polynomial* p) { for (int i = p−>deg; i >= 0; i−−) { if (p−>coeff[i] != 0.0) { p−>deg = i; return; } } p−>deg = 0; } i wywołuje ją przed zwrócenie wielomianu w funkcji dzielącej findDeg(r); findDeg(q); przez co dostaje realne stopnie tychże wielomianów
13 sty 11:12
Filip: Podrzucam ci mój cały kod, jak u mnie wygląda cały program i poszczególne funkcje. Funkcja GCD() coś nie działa poprawnie Tutaj kod: https://pastebin.com/q7Ffu2Ny Pytanie: Chodziło tobie o to, aby nie robić dodatkowej funkcji do sprawdzenia stopni, tylko aby już w funkcji dzielącej te stopnie później zmienić?
13 sty 11:16
Filip: A i zapomniałem, tam w funkcjach gdzie alokujemy pamięć na pomocniczy/nowy wielomian, przydałoby się od razu je tam także zwalniać, lub napisać funkcję, która będzie zwalniać pamięć
13 sty 11:17
Mariusz: Tak chodziło mi o to aby tak zmodyfikować funkcję dzielącą aby reszta miała już taki stopień jaki powinna mieć
13 sty 11:38
Mariusz: Dobra teraz będę pisał inny program, za ok 2h wrócę i spróbuję przetestować twój kod
13 sty 11:53
Filip: Przy okazji, odbiegając od tematu, znasz/spotkałeś się kiedyś z wielomianami Czebyszewa? Dokładniej chodzi mi o aproksymacje średniokwadratową z bazą wielomianów Czebyszewa
13 sty 15:43
Mariusz: Filip , Przetestuj sobie ten kod https://pastebin.com/g8HLnFWr
13 sty 15:46
Mariusz: Wielomiany Czebyszowa , tak średnio używałem ich jedynie do rozwijania cos(nx) jako sumę potęg
13 sty 15:48
Mariusz: Jeszcze ta funkcja licząca NWD nadal nie działa poprawnie
13 sty 17:02
Filip: Potestowałem i działa, wprowadziłem jednak pewną modyfikację w funkcji dodawania i odejmowania pozbyłem się tej ostatniej pętli for na rzecz użycia funkcji findDeg(), jednak przy alokacji dodałem, że n−>deg = newDeg; struct Polynomial* addPolynomials() { /* * kod */ n−>deg = newDeg /* * kod */ findDeg(n); return n; } No i tak samo zrobiłem z funkcją odejmującą wielomiany Jeszcz myślę o przejrzystości tego kodu, sporo alokujemy, może by dodać funkcję, która jako parametr będzie przyjmować stopień wielomianu, zwracającą zaalokowany wielomian − według mnie poprawi to czytelność kodu
13 sty 17:40
Mariusz: https://pastebin.com/g8HLnFWr Jeśli chodzi o funkcję NWD z tego kodu to dotychczas zauważyłem że jeśli znajdzie ten dzielnik w pierwszej iteracji pętli while to zwraca poprawną wartość a jeśli nie znajdzie go w pierwszej iteracji to błędnie zwraca wielomian zerowy chyba że ją poprawiłeś Przykładowa para wielomianów dla funkcji NWD x5−x4−2x3+2x2+x−1 5x4−4x3−6x2+4x+1
13 sty 18:21
Mariusz: Wydaje mi się że poprawiłem ten kod Pamiętasz o tej globalnej stałej o której ci pisałem przy rozwiązywaniu układów równań liniowych Tutaj mi była potrzebna (użyłem zamiast stałej makrodefinicji ale jak ja się uczyłem C to tak się pisało) Dodatkowo alokację pamięci na tablicę dla zmiennych lokalnych dałem wewnątrz pętli while https://pastebin.com/cv0HBfJ4
13 sty 19:51
Mariusz: " Jeszcz myślę o przejrzystości tego kodu, sporo alokujemy, może by dodać funkcję, która jako parametr będzie przyjmować stopień wielomianu, zwracającą zaalokowany wielomian − według mnie poprawi to czytelność kodu" Może to być niezły pomysł z tym poprawieniem czytelności kodu
13 sty 21:18
Filip: No właśnie jaka idea stoi za użyciem tej stałej? Jak rozumiem to jest coś w stylu 0.000000000001? I my to uznajemy jako 0.0? W takim razie co by było gdyby w 154 zamiast fabs(b−>coeff[0]) > ZERO dać fabs(b−>coeff[0]) > 0.0 A no i dlaczego robimy wartość bezwzględną? Ze względu na to, że b−>coeff[0] może być równe −0.0? Kod już potestuje jutro, i jeśli będzie działał poprawnie, to zostało do napisania funkcja obliczająca wartość wielomianu dla jakieś liczby zmiennoprzecinkowej, wtedy będzie można się brać za przejrzystość kodu
13 sty 21:51
Filip: Można jeszcze ciekawiej zrobić, można generować losowo wielomiany, przy pomocy rand(), a raczej ich stopień oraz współczynniki
13 sty 21:58
Mariusz: Chodzi o to że tutaj masz skończoną precyzję i zero w języku programowania nie jest tym samym co zero w matematyce Powinieneś już mieć coś o reprezentacji liczb rzeczywistych na maszynie Gdy będziesz miał metody numeryczne to bardziej będą zwracali uwagę na błędy powstałe podczas wykonywania działań arytmetycznych Dla losowych współczynników NWD najczęściej będzie wielomianem zerowego stopnia Jeśli chodzi o ten kod to gdy po wywołaniu funkcji dzielącej próbowałem podzielić współczynniki reszty przez współczynnik wyrazu wiodącego tejże reszty to zwracał błędny wynik
14 sty 07:28
Filip: Wygląda na to, że GCD() działa poprawnie − tutaj moje pytani, gdybyśmy chcieli napisać funkcję NWW(), to czy sprawdzi się taki zapis?
 P(x)*Q(x) 
NWW=

 NWD(P(x), Q(X) 
I fajnie poprawiłeś sposób wprowadzania wielomianów − teraz jest to o wiele wygodniejsze W sensie co chciałeś podzielić? Wyraz wiodący to jest przykładowo dla wielomianu x2−3x+1, x2? A współczynnik to 1? Czyli chciałeś coś takiego zrobić: dividePolynomials(a, b, r); for (int i = 0; i < r−>deg; i++) r−>coeff[i] /= r−>coeff[r−>deg];
14 sty 13:42
Mariusz: No mniej więcej to chciałem zrobić , oczywiście tylko w funkcji liczącej NWD Co do NWW to dla dwóch wielomianów powinien się sprawdzić Jeśli NWD działa dobrze to możesz użyć naszej strukturki do wyznaczenia mianowników we wzorze Ostrogradskiego do całkowania funkcji wymiernych
14 sty 16:26
Filip: Pytanie bardziej o formatowanie kodu, co sądzisz o blank space'ach w ciałach funkcji? Przepadasz za nimi, czy wszystko jednym ciągiem piszesz od góry do dołu. Umownie jest tak, że jak zaczynasz nowy blok logiczny najlepiej zrobić blank space. Jakie jest twoje stanowisko?
14 sty 18:38
Filip: Ja osobiście w ciele funkcji za nimi nie przepadam
14 sty 18:44
Filip: Zmieniłem troche kod do pewnego momentu, tak to wygląda, pozostałe rzeczy narazie pozostają niezmienione: https://pastebin.com/976pniya
14 sty 18:50
Filip: Niestety zepsułem tym funkcję dzielącą wielomiany, zaraz postaram się znaleźć co jest nie tak...
14 sty 21:10
Filip: A nie, u ciebie w kodzie funkcja dzieląca też nie działa poprawnie
14 sty 21:13
Mariusz: 14 sty 2021 18:38 A to już kwestia umowna Jeśli zwiększą czytelność kodu to czemu nie
14 sty 21:13
Mariusz: A co jest nie tak z funkcją dzielącą ?
14 sty 21:14
Filip: Dziele z resztą wielomiany 2x3−5x2+4x−1 i x−1 i reszta wychodzi mi 0, a powinna −x+1
14 sty 21:17
Filip: ...... nie ważne, reszta to 0
14 sty 21:25
Filip: Rozważałeś użycie realloc() w tutaj zamiast tego kodu? free(a−>coeff); a−>deg = b−>deg; a−>coeff = malloc((a−>deg+1)*sizeof(*a−>coeff)); Na: a−>deg = b−>deg; realloc(a−>coeff, ((a−>deg + 1) * sizeof(*a−>coeff))); Wydaje mi się, że będzie to lepsze niż zwalnianie/alokacja
14 sty 21:32
Mariusz: Może i będzie lepiej wyglądać , a czy będzie lepsze zależy od tego jak napisali funkcję realloc Spróbuj i zabacz jak to działa
14 sty 21:47
Filip: https://pastebin.com/Wqs4S2mg Tutaj masz zmienioną funkcję GCD() − na moje działa, potestowałem, ale też możesz potestować jak nie masz pewności Tym akcentem chyba kończymy temat wielomianów, no chyba że masz jeszcze pomysł jakie funkcje można dodać, moje dwie propozycje: findRoots(struct Polynomial* p) − szuka pierwiastków wielomianu countValue(double x) − oblicza wartość wielomianu dla danego x
14 sty 22:03
Filip: Jednak pierwiastki zbytnio nie wiem jak znaleźć, więc zostaje tylko ta druga funkcja
14 sty 22:11
Mariusz: Jeśli chodzi o znajdowanie pierwiastków to myślałem nad jakimiś metodami numerycznymi a jeśli chodzi o tę drugą funkcję to może schemat Hornera Jeśli chodzi o pierwiastki to musiałbyś trochę poczytać o metodach numerycznych Co do schematu Hornera to stosunkowo łatwo go napisać Możemy do tego powrócić jak będziesz miał metody numeryczne lub o tym trochę poczytasz
14 sty 22:20
Filip: No dobra, w takim razie wątek chyba tymczasowo zakończony, jak masz jeszcze jakieś fajne tematy, które można tak omówić to możesz w przyszłości zakładać nowe wątki, ja raczej na forum jestem emotka Ja chyba tym czasem będę zabierał się za listę dwukierunkową
14 sty 22:35
Mariusz: Listę dwukierunkową − kiedyś ją pisałem https://ideone.com/nubXls O tutaj zrealizowałem nawet algorytm Jarvisa na liście https://pastebin.com/VpNqNHuS Co do kodu w C# to tutaj wstawianie w sposób uporządkowany nie jest optymalne bo przepisywałem tę funkcję z listy jednokierunkowej Zastanów się nad wstawianiem przed wybranym elementem Rozpatrz przypadki jakie możesz otrzymać i oprogramuj je
14 sty 22:59
Mariusz: A co z całkami na metodę Ostrogradskiego i podstawienia Eulera ? Będziemy ten temat kontynuować ?
14 sty 23:06
Filip: Można, tylko właśnie utknąłem na tej drugiej całce:
 3x+1 

dx
 (x−1)(x2+1) 
Możesz jakąś podpowiedź dać?
14 sty 23:59
Mariusz: Możesz rozkładać na sumę ułamków prostych a możesz zauważyć że (x2+1)−(x−1)2=2x (x2+1)−(x−1)(x+1)=2 a zatem
3 3 

(x2+1)−

(x−1)(x−1)=3x
2 2 
1 1 

(x2+1)−

(x−1)(x+1)=1
2 2 
 1 
3x+1=2(x2+1)−

(x−1)(3x−3+x+1)
 2 
 1 
3x+1=2(x2+1)−

(x−1)(4x−2)
 2 
3x+1=2(x2+1)−(x−1)(2x−1) Może z tematami dotyczącymi całek przejdźmy do wątku https://matematykaszkolna.pl/forum/406673.html Tematy dotyczące programowania też chcesz kontynuować w innym wątku ? bo wynikałoby to z wpisu z 14 sty 2021 22:35 Obejrzałeś tę listę co podesłałem ?
15 sty 00:15
Filip: Tak, jak widziałeś już tam powróciłem Kod widziałem, widzę że umiesz C++ na przyjemnym poziomie, sam kiedyś sporo siedziałem w C++, jednak już trochę zapomniałem, w sumie mogę "przetłumaczyć" twój kod w C++ na kod w C Wstawianie elementu przed wybranym elementem, zakładam, że jako argument funkcji podajemy naszą listę oraz numer elementu (indeksujemy od 0, czy 1 elementy?) Przykładowo A B C D <− lista wstaw(lista, 1, P) P A B C D czy A P B C D − zależy od indeksowania Jak widzę, będą tutaj trzy przypadki − skoro wstawiamy przed jakiś element, na sam tył listy nie możemy wsadzić Czy zakładamy że w liście jest już co najmniej jeden element? Jeśli tak, to trzeba tylko dwa przypadki rozważyć (1) Gdy wstawiamy przed pierwszy element (2) Gdy wstawiamy w dowolnym miejscu (poza pierwszym elementem) No i gdybyśmy nie wiedzieli jakiej wielkości jest nasza lista, trzeba dodać jeszcze sprawdzenie if (list == NULL) To w sumie skoro nie ma elementów, to nie można wstawić przed element, więc można to na początku funkcji wstaw() umieścić (tego if'a) Tak bym to widział, daj znać czy jest to poprawne rozumowanie
15 sty 01:22
Mariusz: A to ty indeksujesz listę ? Ja myślałem że będziesz wyszukiwał węzeł o podanym kluczu i wstawiał węzeł przed tym znalezionym Gdy rozważysz trzy przypadki to wtedy ciało naszej funkcji będzie można wykorzystać do napisania funkcji wstawiającej do listy w sposób uporządkowany Jak patrzyłeś w kod C# to tam mam też funkcję wstawiającą w sposób uporządkowany ale przepisaną z listy jednokierunkowej dlatego zawiera jeden dodatkowy wskaźnik (do wersji dla listy jednokierunkowej dopisałem ustawianie wskaźników na poprzedników)
15 sty 09:12
Mariusz: A co do naszej strukturki wielomianów to kiedyś pisałem program wyznaczający wielomian interpolacyjny Lagrange https://pastebin.com/4x1GgTUT
15 sty 09:21
Filip: Indeksowanie, nie wiem jak to nazwać, chodziło mi o to, że jak mam listę A B C D to A jest na pierwszym miejsce tej listy (od lewej) B na drugim itd W sensie chodzi o to, że mamy listę A B C D A − klucz 1 B − klucz 2 . . Xi − klucz i−ty Czy chodzi o to, że załóżmy mamy strukturę taką (od ciebie wezmę na kod C) struct Link { int data; struct Link* next; struct Link* prev; } Wywołujemy funkcję wstaw(list, a, 3); gdzie: list − nasza lista a − obiekt klasy Link 3 − klucz To szukamy pierwszego elementu dla ktorego l−>data == 3 czy liczymy elementy i wstawiamy przed trzecim?
15 sty 12:16
Mariusz: Chodzi o to że funkcja wstawiająca mogłaby mieć taki nagłówek template<class T> void DoublyLinkedList::insertBefore(T key,T data); Teraz funkcja template<class T> Link<T> *DoublyLinkedList::find(T key); zwraca wskaźnik na węzeł przed którym należy wstawić nowy węzeł i wg mnie dobrze by było rozpatrzeć trzy przypadki 1) gdy funkcja wyszukująca zwróci NULL 2) gdy funkcja wyszukująca zwróci wskaźnik na głowę 3) gdy funkcja wyszukująca zwróci inny wskaźnik
15 sty 13:52
Mariusz: Ej bawiłeś się wskaźnikiem void * ? W C++ mamy szablony Jeśli tutaj chciałbyś coś podobnego zrealizować to trzeba by było się tymi wskaźnikami (void *) pobawić
15 sty 13:57
Filip: Dokładnie te 3 przypadki bym rozpatrzył, odnośnie tych przypadków: (1) Gdy lista jest pusta to po prostu zwracamy jakąś notkę, że lista jest pusta (2) Jeżeli chcemy insertować na pierwsze miejsce listy. to by to mniej więcej tak wyglądało? Załóżmy że ten element to obiekt listy o nazwie el head−>prev = el−>next; el−>prev = NULL; el−>next = head; (3) Tutaj by trzebabyło wyszukać, w które miejsce wstawić − przykładowo przejść po liście z jakims licznikiem: struct list* l = head; int counter = 0; while (counter != place − 1) { l = l−>next; counter++; } l−>prev = el; el−>next = l l = l−>prev; l−>next = el; el−>prev = l; Ja bym to mniej więcej tak widział, oczywiście możesz potwierdzić czy idzie to w dobrą stronę Bawiłem się jedynie z const void* podczas pisania funkcji porównawczej do sortowania, W C++ szablony to bardzo przydatna rzecz, nawet nie wiedziałem że void* w C może odpowiadać szabloną z C++
15 sty 15:38
Mariusz: Ja to widzę tak jak to napisałem 15 sty 2021 13:52 Wyszukujemy węzeł o podanym kluczu , funkcja wyszukująca zwróci nam wskaźnik na znaleziony węzeł Jeśli zwróci NULL to lepiej chyba wstawić na koniec bo wtedy będziemy mogli wykorzystać tę funkcję do wstawiania w sposób uporządkowany (wstawiamy przed NULLem) Wprowadzanie liczników ogranicza liczbę węzłów − znowu liczby całkowite w programowaniu to nie to samo co liczby całkowite w matematyce Lista bez licznika jest ograniczona tylko ilością dostępnej pamięci Lista z licznikiem jest ograniczona także zakresem typu int
15 sty 16:13
Filip: Zapomniałem o tej funkcji find() w momencie pisania, właśnie patrzę na jej kod w twoim kodzie z C++, w takim razie z tego idzie wywnioskować, że klucz to jest po prostu value, to można z tego licznika zrezygnować
15 sty 16:30
Mariusz: Tak właśnie węzeł przed którym można nowy węzeł wstawić można wyszukać wcześniej napisaną funkcją wyszukującą węzeł o danym kluczu
15 sty 17:57
Filip: No to mi się wydaje, że schemat postępowania będzie taki: (1) Jeżeli find() zwróci wartość NULL (1.1) Jeżeli lista główna nie posiada elementów (1.1.1) zwróć odpowiedni komunitak (1.2) Inaczej dodaj element na koniec listy (1.2.1) wyjdz z funkcji (2) Jeżeli insertujemy na początek listy head−>next−>prev = el el−>next = head−>next el−>prev = NULL no i (3) warunek, zobacze jakby to w kodzie w C wyglądało i podeśle jak coś zlepie
15 sty 18:14
Filip: W kodzie zapisałbym to tak: (1) funkcja zwracająca wskaźnik na konkretny węzeł struct list* find(struct list* l, int key) { struct list* temp = l; while (temp != NULL && temp−>value != key) { temp = temp−>next; } return temp; } (2) funkcja wstawiająca nowy węzeł przed podanym kluczem void insertAt(struct list *l, int key, struct list* el) { struct list* b = find(key); struct list* temp = l; if (key == NULL) { if (temp == NULL) return; while (temp−>next != NULL) temp = temp−>next; temp−>next = el; el−>prev = temp; el−>next = NULL; } else if (temp == b) { temp−>prev = el; el−>next = temp; el−>prev = NULL; } else { while (temp != b) temp = temp−>next; temp−>prev−>next = el; temp−>prev = el; el−>prev = temp−>prev; el−>next = temp; } } Ja bym to tak widział
15 sty 20:09
Mariusz: struct List { struct Node *head; struct Node *tail; }; struct Node { int data; struct Node *prev; struct Node *next; }; struct Node *find(struct List L,int key) { struct Node *temp = L.head; while(temp != NULL && temp−>data != key) temp = temp−>next; return temp; } void insertBefore(struct List *L,int key int d) { struct Node *newNode = (struct Node *)malloc(sizeof(struct Node )); newNode−>data = d; struct Node *temp = find(*L,key); if(temp == NULL) { newNode−>next = NULL; if(L−>head == NULL) L−>head = newNode; else L−>tail−>next = newNode; newNode−>prev = L−>tail; L−>tail = newNode; } else if(temp−>prev == NULL) { newNode−>prev = NULL; if(L−>head == NULL) L−>tail = newNode; else L−>head−>prev = newNode; newNode−>next = L−>head; L−>head = newNode; } else { temp−>prev−>next = newNode; newNode−>next = temp; newNode−>prev = temp−>prev; temp−>prev = newNode; } } No ja tak to widzę
15 sty 21:02
Filip: Mój kod ma trochę błędów, postaram się poprawić, pytani − po co rozbijać to na strukturę List oraz Node? Tak trzeba czy to jakaś prywatna preferencja?
15 sty 21:12
Mariusz: Każda lista dwukierunkowa reprezentowana jest przez dwa wskaźniki − na głowę i na ogon i ja po prostu opakowałem te wskaźniki w strukturę Wskaźnik na ogon przydaje się do wstawiania na koniec i do wstecznej iteracji
15 sty 21:26
Filip: Ok, czyli jakby to w jedną strukturę zapakować to by wyglądała tak? struct List { int data; struct List* next; struct List* prev; struct List* head; struct List* tail; }
15 sty 21:39
Mariusz: Nie jestem przekonany do takiego rozwiązania bo tak to każdy węzeł miałby pole head i tail Przyjrzyj się wersji z C++ albo C# tam też masz oddzielnie klasę dla listy i klasę dla węzła Choć z drugiej strony w C++ można było zagnieździć klasę węzła w klasie listy tylko nie jestem pewny jak to będzie wtedy z szablonami gdy zagnieździmy klasę węzła w klasie listy Sam jestem ciekawy czy ten pomysł będzie dobry
15 sty 21:49
Mariusz: Gdybyśmy chcieli to napisać w jednej strukturce to może w ten sposób ? struct List { struct Node { int data; struct Node *next; struct Node *prev; }; struct Node *head; struct Node *tail; };
15 sty 21:57
Filip: hmm to chyba już rozumiem, list−>head wskazuje na pierwszy Node, a list−>tail na ostatni, czyli przykładowo jak zrobie: struct List* l = head; I moge teraz zrobić l−>data, l−>next oraz l−>prev Właśnie do końca nie wiem jak działa zagnieżdżanie w sobie struktur
15 sty 22:09
Filip: Tak właściwie to nie zagnieżdżanie struktury, tylko taki przykład jak podałeś nazywa się po angielsku: "embedded structure" − nie wiem jak to na język polski przetłumaczyć
15 sty 22:12
Filip: Jednak chyba aby odwołać się do węzła to w taki sposób struct List* l; l−>Node−>value
15 sty 22:17
Filip: Nie, takie odwołanie nie wchodzi w gre − w takim razie jak się odwołać do takiej struktury?
15 sty 22:26
Filip: Jednak się da, trzeba troche przerobić struktury struct List { struct Node { int data; struct Node *next; struct Node *prev; }* Node; struct Node *head; struct Node *tail; }; I teraz: struct List* l = malloc(sizeof(*l)); l−>Node = malloc(sizeof(l−>Node)); I teraz można się odnosić: l−>Node−>data = 5; l−>node−>prev = NULL; l−>node−>next = NULL;
15 sty 22:33
Mariusz: Na razie masz tyle Funkcję main sobie dopisz i przetestuj te funkcje które masz #include<stdio.h> #include<stdlib.h> struct List { struct Node { int data; struct Node *prev; struct Node *next; }; struct Node *head; struct Node *tail; }; void Init(struct List *L) { L−>head = NULL; L−>tail = NULL; } int IsEmpty(struct List L) { return (L.head == NULL); } void DisplayForward(struct List L) { struct Node *temp = L.head; printf("Lista : (pierwszy −−> ostatni) "); while(temp != NULL) { printf("%d ",temp−>data); temp = temp−>next; } printf("\n"); } void DisplayBackward(struct List L) { struct Node *temp = L.tail; printf("Lista : (ostatni −−> pierwszy) "); while(temp != NULL) { printf("%d ",temp−>data); temp = temp−>prev; } printf("\n"); } void Push(struct List *L,int d) { struct Node *newNode = (struct Node *)malloc(sizeof(struct Node )); newNode−>data = d; newNode−>prev = NULL; if(IsEmpty(*L)) L−>tail = newNode; else L−>head−>prev = newNode; newNode−>next = L−>head; L−>head = newNode; } void Append(struct List *L,int d) { struct Node *newNode = (struct Node *)malloc(sizeof(struct Node )); newNode−>data = d; newNode−>next = NULL; if(IsEmpty(*L)) L−>head = newNode; else L−>tail−>next = newNode; newNode−>prev = L−>tail; L−>tail = newNode; } void Pop(struct List *L) { if(IsEmpty(*L)) return; struct Node * temp = L−>head; if(L−>head−>next == NULL) L−>tail == NULL; else L−>head−>next−>prev = NULL; L−>head = L−>head−>next; free(temp); } struct Node *Find(struct List L,int key) { struct Node *temp = L.head; while(temp != NULL && temp−>data != key) temp = temp−>next; return temp; } int main() { struct List L; return 0; }
15 sty 23:02
Mariusz: No to może przepisz sobie te funkcje z mojego kodu w C++ na C po swojemu a jak nie będzie działać to pomyślimy co jest nie tak
15 sty 23:08
Filip: Ok, tylko chce zrozumieć idee head i tail: Mamy listę A B C D E head wskazuje na A tail na element E
15 sty 23:27
Filip: Może zamiast robić zagnieżdżanie, trzymajmy się tego zapisu: struct List { struct Node *head; struct Node *tail; }; struct Node { int data; struct Node *prev; struct Node *next; };
15 sty 23:33
Mariusz: ad 15 sty 2021 23:27 no tak właśnie jest ad 15 sty 2021 23:33 taką reprezentację listy zaproponowałem na samym początku , więc jeśli ją wolisz możemy przy niej zostać
15 sty 23:41
Filip: void insertAt(struct List* l, int key, struct Node* el) { struct Node* temp = find(l, key); if (temp == NULL) { el−>next = NULL; if (l−>head == NULL) { l−>head = el; } else { l−>tail−>next = el; } l−>tail = el; el−>prev = l−>tail; } else if (temp−>prev == NULL) { el−>prev = NULL; if (l−>head == NULL) { l−>tail = el; } else { l−>head−>prev = el; } el−>next = l−>head; l−>head = el; } else { temp−>prev−>next = el; el−>next = temp; el−>prev = temp−>prev; temp−>prev = el; } } Napisałem coś takiego, jak teraz patrzę ten kod prawie się nie różni od twojego powyższego z 15stycznia 21:02
15 sty 23:57
Filip: No i funkcja find() struct Node* find(struct List* l, int key) { struct Node* temp = l−>head; while (temp != NULL && t−>data != key) temp = temp−>next; return temp; } Zapomniałem zmienić nazwy, bo może być myląca "InsertAt", powinno być "insertBefore" tak jak u ciebie
15 sty 23:58
Mariusz: InsertAt to jest dobra nazwa dla listy z licznikiiem
16 sty 00:37
Filip: Jak mamy tak właściwie inserty do listy, no poza instertBack, ale kod do funkcji insertBefore zapewne będzie podobny, to może teraz jakieś usuwanie elementu? Widzę że masz metody deleteKey(), deleteFirst(), deleteSecond()
16 sty 00:51
Mariusz: Takie operacje usuwające tam są deleteKey(), usuwa węzeł o danym kluczu deleteFirst() usuwa pierwszy węzeł deleteLast() usuwa ostatni węzeł
16 sty 00:56
Filip: Racja, pomyliłem się, chodziło oczywiście o deleteLast(). Pytanie: O co chodzi z tymi punktami, które masz w programie?
16 sty 01:04
Mariusz: Masz dany zbiór punktów na tej liście i chcesz narysować wielokąt wypukły o możliwie najmniejszej liczbie boków który zawierałby wszystkie podane punkty
16 sty 01:24
Mariusz: Co do funkcji których nie ma w programie w C++ to jak zrealizowałbyś konkatenację dwóch list ?
16 sty 08:09
Filip: Załóżmy, że listy to a i b To czy wyjściowa to będzie nowa lista c = a + b czy a = a + b?
16 sty 11:57
Mariusz: Niech funkcje enqueue , dequeue oznaczają operacje wstawiania i usuwania z kolejki a concat oznacza operację konkatenacji dwóch list Rozważmy następującą funkcję void function(struct List *L) { struct List A; struct List B; struct List C; struct Node *temp; struct Node *p; if(L−>head != L−>tail) { A.head = NULL; A.tail = NULL; B.head = NULL; B.tail = NULL; C.head = NULL; C.tail = NULL; p = L−>tail; while(L−>head != NULL) { temp = dequeue(L); if(temp−>data < p−>data) enqueue(A,temp); else if(temp−>data == p−>data) enqueue(B,temp); else enqueue(C,temp); } function(&A); function(&C); concat(L,A); concat(L,B); concat(L,C); } } Co ta funkcja powinna robić ? Jeśli chodzi o konkatenację to myślałem aby ją tak napisać aby można było jej użyć w wygodny sposób w powyższej funkcji
16 sty 12:48
Filip: W funkcji concat(a, b) Na pierwszą myśl przychodzi rozpatrzenie warunku, co jeśli a lub b jest jest puste if (b == NULL) return; If (a == NULL) { a−>head = b−>head; a−>tail = b−>tail; } Tak bym to na początku widział, czyli jeśli lista która chcemy "doczepić" jest pusta, nic nie robimy, jeśli lista do której chcemy doczepić jest pusta, ustawiamy jej head i tail na początek i koniec listy b
16 sty 12:59
Filip: A no i można jeszcze w drugim ifie dodać b−>head = NULL; b−>tail = NULL; czy tak?
16 sty 13:02
Mariusz: Jeśli jedna z list jest pusta to konkatenacja zwraca tę drugą No a teraz rozpatrz pozostałe przypadki Wspomniałem o konkatenacji bo można by jej użyć do napisania funkcji którą napisałem 16 sty 2021 12:48
16 sty 15:10
Filip: Czy ta funkcja function() to jest funkcja sortująca listę, z wyborem ostatniego elementu w danej liście, który służy za pivot?
16 sty 15:21
Filip: Przykładowo dla listy wejściowej 12 34 4 5 6 121 3 20 pivotem bedzie 20 Do listy A idą elementy mniejsze niż 20 Do listy B ida elementy równe 20 Do listy C idą elementy większe niż 20 Przed wywołaniem rekurencyjnym będzie to wyglądać tak: Lista A 12 4 5 6 3 Lista B NULL Lista C 34 121 No i po wywołaniu rekurencyjnym dajmy na to dla listy A 4 − pivot Lista A 3 Lista B NULL Lista C 12 4 5 6
16 sty 15:24
Mariusz: Tak to o to chodziło z tym że w twoim przykładzie na liście B będzie jeden element ten z kluczem równym 20 A to po wywołaniu rekurencyjnym dla listy A pivotem nie będzie 3 wtedy Lista A NULL Lista B 3 Lista C 12 4 5 6 A co z konkatenacją i operacjami na kolejce ?
16 sty 16:02
Filip: dequeue() − ściąga nam pierwszy element z naszej kolejki, przykładowo, mamy taką kolejkę Q = A B C D E Q−>head = A Q−>tail = E czyli po wywołaniu funkcji dequeue(Q) dostaniemy nasz element, który zdejmujemy − czyli A, a kolejka wygląda tak: Q = B C D E Q−>head = B Q−>tail = E W kodzie może to wyglądać tak: struct Node* n = Q−>head; Q−>head = n−>next; n−>next−>prev = NULL; return n;
16 sty 16:20
Filip: A no i czy nie powinnismy w funkcji zwalniać pamięci elementu, którego bierzemy?
16 sty 16:22
Mariusz: Jeśli używamy operacji na kolejce w funkcji sortującej listę to nie a jeśli chcesz użyć tych funkcji aby utworzyć kolejkę to powinieneś zwalniać pamięć
16 sty 16:56
Filip: Funkcja enqueue będzie wyglądać podobnie, tyle że będziemy dodawać element na koniec: struct Node* n = Q−>tail n−>next = el el−>prev = n; el−>next = NULL Q−>tail = el Pytanie, co jeśli mi wywołamy funkcję enqueue() / dequeue() i okaże się, że: if (Q−>tail == NULL && Q−>head == NULL) wejdzie nam w takiego ifa Wydaje mi się, że to trzeba sprawidzć, chociaż po kodzie widzę, że za pierwszym razem nam w tego ifa wejdzie − wtedy rozważamy taki przypadek: if (Q−>tail == NULL && Q−>head == NULL) { temp−>prev = NULL; temp−>next = NULL; Q−>tail = temp; Q−>head = temp } Czy dobrze to rozumiem? Czyli pozostaje do napisania funkcja concat()
16 sty 17:40
Mariusz: A to funkcje do obsługi kolejki nie powinny wyglądać jakoś podobnie do tych insertLast() oraz DeleteFirst() w kodzie który napisałem w C++ https://ideone.com/nubXls No co z konkatenacją ? Jak proponujesz ją zrealizować ?
16 sty 18:16
Mariusz: Funkcje equeue() oraz dequeue() napisane tak aby można ich było użyć w funkcji sortującej void enqueue(struct List *L,struct Node *x) { x−>next = NULL; if(L−>head == NULL) L−>head = x; else L−>tail−>next = x; x−>prev = L−>tail; L−>tail = x; } struct Node * dequeue(struct List *L) { if(L−>head == NULL) return NULL; struct Node *temp = L−>head; if(L−>head−>next == NULL) L−>tail = NULL; else L−>head−>next−>prev = NULL; L−>head = L−>head−>next; return temp; }
16 sty 18:44
Filip: void concat(struct* List a, struct* List b) { if (b−>head == NULL) return; if (a−>head == NULL) { a−>head = b−>head; a−>tail = b−>tail; b−>head = NULL; b−>tail = NULL; return; } struct Node* last = a−>tail; struct Node* first = b−>head; last−>next = first; first−>prev = last; a−>tail = b−>tail; b−>head = NULL; b−>tail = NULL; } Tak?
16 sty 18:47
Mariusz: Trochę pobawiłem się twoim kodem i wygląda ok
16 sty 20:18
Mariusz: No może poza błędnym położeniem asteriska na liście parametrów funkcji
16 sty 20:25
Filip: Racja, tam jest błąd
16 sty 21:14
Filip: Mariusz zajrzysz do wątku z całkami? Obliczyłem tam kolejne dwie, jeszcze z 4 przykłady zostały z metody Ostrogradskiego i można przejść do podstawienia Eulera
16 sty 21:31
Filip: Jak masz jeszcze jakieś ciekawe zagadnienia z programowania, które można omówić to podrzucaj emotka (o ile poprzednie uważasz za zakończone)
16 sty 22:19
Mariusz: A pozostałe funkcje do obsługi listy dasz radę napisać ?
16 sty 22:23
Filip: Napisać to napiszę, gorzej u mnie z testowaniem, możemy umówić się tak, że zrobię spis na podstawie tego co mam i na podstawie twojego kodu, których metod mi jeszcze brakuje i postaram się je napisać po kolei, i tutaj wklejać do ewentualnej weryfikacji
16 sty 22:41
Filip: No i oczywiście (na marginesie) całki konttynuujemy
16 sty 22:48
Filip: Zostało mi do napisania te metody, zdaje mi się, że to wszystkie: Link<T> *getFirst(); void setFirst(Link<T> *first); Link<T> *getLast(); void setLast(Link<T> *first); bool isEmpty(); Link<T> *find(T key); void insertFirst(T dd); void insertLast(T dd); void deleteFirst(); void deleteLast(); bool insertAfter(T key,T dd); void deleteKey(T key);
17 sty 16:47
Mariusz: Z getterów i setterów możesz zrezygnować Przeglądanie listy już masz ?
18 sty 02:31
Filip: W sensie wypisywanie elementów od przodu jak i od tyłu?
18 sty 12:19
Mariusz: tak
18 sty 15:45
Filip: Zorientowałem się, że nie mam nawet dodawania elementu na koniec list Wypisywanie elementy od przodu zrealizowałbym tak: void print(struct List* l) { struct Node* n = l−>head; while (n != NULL) { printf("%d ", n−>data); n = n−>next; } } A od tyłu to tak: void print(struct List* l) { struct Node* n = l−>tail; while (n != NULL) { printf("%d ", n−>data); n = n−>prev; } }
18 sty 16:06
Filip: A, no i inna nazwa metody, przykładowo printBackwards
18 sty 16:09
Filip: dodawanie do listy (na koniec) zrealizowałbym tak: void addElement(struct List* l, struct Node* n) { if (l−>head == NULL) { //pusta lista l−>head = n; l−>tail = n; } else { struct Node* temp = l−>tail; temp−>next = n; l−>tail = n; n−>prev = temp; } }
18 sty 16:28
Mariusz: No tak do tego utworzenie węzła
18 sty 19:51
Filip: Hmm racja,, powinienem sprawdzać warunek, gdy n == NULL, albo trochę zmodyfikować funkcję void addElement(struct List* l, int data) { struct Node* n = malloc(sizeof(*n)); n−>data = data; n−>next = NULL; n−>prev = NULL; if (l−>head == NULL) { //pusta lista l−>head = n; l−>tail = n; } else { struct Node* temp = l−>tail; temp−>next = n; l−>tail = n; n−>prev = temp; } }
18 sty 20:16
Filip: Mariusz troche odbiegając od tematy, załóżmy że mam taki wzór rekurencyjny T0=0 T1=1 Tn(x)=2xTn−1(x)−Tn−2(x) Napisałem funkcję, która przyjmuje x − argument jakiejś funkcji, oraz n − wielomian n−tej pozycji że tak to nazwę double U0(double x){ return 1; } double U1(double x){ return x; } double Un(double x, int n){ if(n==0){ return U0(x); } else if(n==1){ return U1(x); } else{ return 2*x*Un(x,n−1)−Un(x,n−2); } } Wygląda ona tak, przykładowo dla x =1 i dla n = 1 zwróci mi ona wartośc 1 Masz jakiś pomysł, aby liczyło mi 1, 2, oraz 3 pochodną z n−tego wzoru? Przykładowo dla x = 1, n = 5, aby liczyło mi: (T5)'(x)= (T5)''(x)= (T5)'''(x)=
19 sty 14:07
Filip: Może da się znaleźć wzór rekurencyjny na pochodne?
19 sty 14:10
Filip: T1=x*
19 sty 14:10
Filip: Znalazłem wzór tutaj: https://www.degruyter.com/view/journals/math/15/1/article-p1156.xml Dla wielomianów pierwszego rodzaju, wiesz może jak to zaimplementować?
19 sty 14:17
Mariusz: T0=0 T1=1 Tn(x)=2xTn−1(x)−Tn−2(x) G(x,t)=∑n=0Tn(x) ∑n=2Tn(x)tn=∑n=2(2xTn−1(x)tn)−∑n=2Tn−2(x)tnn=2Tn(x)tn=2xt(∑n=2(Tn−1(x)tn−1))−t2(∑n=2Tn−2(x)tn−2) ∑n=2Tn(x)tn=2xt(∑n=1(Tn(x)tn))−t2(∑n=0Tn(x)tn) ∑n=0Tn(x)tn−0−t=2xt(∑n=0(Tn(x)tn)−0)−t2(∑n=0Tn(x)tn) G(x,t)−t=2xtG(x,t)−t2G(x,t) G(x,t)(1−2xt+t2)=t
 t 
G(x,t)=

 1−2xt+t2 
t t 

=

1−2xt+t2 (1−xt)2−(x2−1)t2 
t t 

=

1−2xt+t2 (1−(x−x2−1)t)(1−(x+x2−1)t) 
t A B 

=

+

1−2xt+t2 1−(x−x2−1)t 1−(x+x2−1)t 
A−A(x+x2−1)t+B−B(x−x2−1)t=t A+B=0 A(x+x2−1)+B(x−x2−1)=−1 B=−A A(x+x2−1)+B(x−x2−1)=−1 A(x+x2−1)−A(x−x2−1)=−1 A(2x2−1)=−1
 1 
A=−

 2x2−1 
 1 
B=

 2x2−1 
G(x,t)=
 1 1 

(∑n=0(x−x2−1)ntn)+

n=0((x+x2−1)tn)
 2x2−1 2x2−1 
 1 1 
G(x,t)=∑n=0(−

(x−x2−1)n+

(x+x2−1)n)tn
 2x2−1 2x2−1 
 1 1 
Tn(x)=−

(x−x2−1)n+

(x+x2−1)n
 2x2−1 2x2−1 
Może z tej postaci łatwiej będzie policzyć pochodną
19 sty 15:19
Mariusz: Dla wielomianów pierwszego rodzaju masz tę sam wzór rekurencyjny ale inne warunki początkowe Zdaje się że T0=1 T1=x
19 sty 15:24
Filip: Hmm, kurcze ty założyłeś, że T1=1, jednak to pózniej poprawiłem, że T1=x, czy to zmieni ten wzór rekurencyjny i wynik finalny?
19 sty 15:26
Filip: No tak, z tego mogę obliczyć pierwszą pochodną n−tego wielomianu, a da się znaleźć wzór na m−tą pochodną n−tego wielomianu?
19 sty 15:32
Filip: Nie wiem czy patrzyłeś w link co ci podesłałem, jest tam wzór, ale skomplikowany, może da go się jakoś uprościć, aby dało się zaimplementować
19 sty 15:36
Mariusz: Ten wzór co ty podałeś jest dobry dla wielomianów drugiego rodzaju Ja podałem warunki początkowe dla wielomianów pierwszego rodzaju bo wzór rekurencyjny jest ten sam dla obydwu Patrzyłem na wzór z wpisu 19 sty 2021 14:07 Nie uwzględniłem twojej poprawki
19 sty 15:38
Mariusz: Próbowałeś użyć naszej strukturki i napisać funkcję rekurencyjną która zwracałaby n. ty wielomian Czebyszowa
19 sty 15:45
Filip: No właśnie z tym miałem problem, dlatego od razu stwierdziłem że będę liczyć wartość dla n−tego wielomianu
19 sty 16:01
Filip: Hmm no tak, mamy strukturę przecież, czyli w takim razie jakby to wyglądało: tworzy sobie tablicę kolejnych wielomianów: T0=0 T1=1 I po prostu robimy na początku mnożenie t[n]=2x*Tn−1(x) + Tn−2(x) Hmm, mamy tablicę obiektów struct Polynomial t[10000]; struct Polynomial p; p.deg = 1 p.coeff[1]=2; p.coeff[0]=0 t[0]=1; t[1]=x; t[n]= multiplyPolynomials(p, t[n−1]) t[n]=addPolynomials(t[n], t[n−2]); Tak by to mniej więcej wyglądało?
19 sty 16:07
Filip: a te wielomiany trzeba stworzyć Zapomniałem o mallocach() i oczywiscie o stworzeniu wielomianow 1 oraz x, ale mniej wiecej mam nadzieje ze przekazałem ci patent jak to ma wygladac
19 sty 16:09
Filip: W sensie jakbym to w kodzie napisał
19 sty 16:09
Mariusz: Jak chcesz skorzystać z ich wyników to musiałbyś kilka funkcyj pomocniczych napisać takich jak np dolna silnia czy symbol Newtona no i funkcję która działałałaby na naszej strukturce i zwracałaby wielomian Czebyszowa Ty masz t[n]=2x*Tn−1(x) + Tn−2(x) Powinno być t[n]=2x*Tn−1(x) − Tn−2(x); Dopisz do naszej strukturki schemat Hornera
19 sty 16:39
Filip: Dlaczego to co napisałem by nie zadziałało bez tych dodatkowych funkcji? Po co schemat Hornera bo nie widzę?
19 sty 16:41
Mariusz: Napisałem że gdybyś chciał skorzystać z ich wyników to byś musiał dopisać te dodatkowe funkcję Gdybyś chciał pisać po swojemu to nie musisz ich pisać Napisałeś że chciałeś policzyć wartość wielomianu − do tego przyda się schemat Hornera
19 sty 16:56
Mariusz: A i zastanów się czy potrzebujesz całej tablicy tych wielomianów czy może wystarczy ich mniej W każdej iteracji tworzysz wielomian k+1. stopnia i potrzebujesz do tego dwóch poprzednich wielomianów nie musisz pamiętać wszytkich chyba że z założenia chcesz pamiętać wszytkie
19 sty 17:01
Filip: Chce pamiętać wszystkie, bo dla n=10, mam 11 wielomianów i od każdego z nich liczę wartość 1 i 2 i 3 pochodnej dla jakiegoś x
19 sty 17:03
Filip: Zaraz mam kolokwium z fizyki, wrócę tutaj po kolokwium i postaram się coś popisać w kodzie
19 sty 17:03
Filip: Napisałem taki kodzik, generalnie działa i wyświetla mi n kolejnych wielomianów Czebyszewa int n = 20; struct Polynomial* p = malloc(sizeof(*p)); p−>deg=1; p−>coeff=malloc((p−>deg+1)*sizeof(double)); p−>coeff[0]=0; p−>coeff[1]=2; struct Polynomial* c[100]; c[0]=malloc(sizeof(struct Polynomial)); c[0]−>deg=0; c[0]−>coeff = malloc(sizeof(double)); c[0]−>coeff[0]=1; c[1]=malloc(sizeof(struct Polynomial)); c[1]−>deg=1; c[1]−>coeff = malloc(2*sizeof(double)); c[1]−>coeff[0]=0; c[1]−>coeff[1]=1; for (int i = 2; i < n; i++) { c[i]=malloc(sizeof(*c[i])); c[i]=multiplyPolynomials(p, c[i−1]); c[i]=substractPolynomials(c[i], c[i−2]); } for (int i = 0; i < n; i++) { printf("T%d\t", i); printPolynomial(c[i]); printf("\n"); }
19 sty 18:14
Filip: No i teraz została do napisania funkcja która przykładowo taki szablon będzie mieć struct Polynomial* ddx(struct Polynomial* p, int i) gdzie i to i−ta pochodna
19 sty 18:17
Filip: Przykładowy output dla n=10 T0 +1.000000x0 T1 +1.000000x1 T2 +2.000000x2 −1.000000x0 T3 +4.000000x3 −3.000000x1 T4 +8.000000x4 −8.000000x2 +1.000000x0 T5 +16.000000x5 −20.000000x3 +5.000000x1 T6 +32.000000x6 −48.000000x4 +18.000000x2 −1.000000x0 T7 +64.000000x7 −112.000000x5 +56.000000x3 −7.000000x1 T8 +128.000000x8 −256.000000x6 +160.000000x4 −32.000000x2 +1.000000x0 T9 +256.000000x9 −576.000000x7 +432.000000x5 −120.000000x3 +9.000000x1
19 sty 18:19
Mariusz: Dla i = 0 zwracasz wielomian p Następnie starasz się zbudować rekurencję wiedząc że Pierwsza pochodna to dp−>deg = p−>deg − 1; dp−>coeff = malloc((dp−>deg+1)*malloc(*dp−>coeff)); for(int k = 0;k <= dp−>deg; k++) dp−>coeff[k] = (k+1)*p−>coeff[k+1]; oraz z tego że n. tą pochodną da się zapisać rekurencyjnie
19 sty 19:18
Mariusz: Zastanawiałeś się nad podzieleniem tego programiku do obsługi wielomianów na plik nagłówkowy (*.h) oraz na plik (*c)
19 sty 19:21
Filip: Tak, generalnie program w którym użyje tej struktury wielomianów składa się z kilku c i h, więc tego też przerobie na potrzebne mi funkcje
19 sty 19:54
Filip: Musze przenalizowac twój kod do pochodnej, troche inaczej go pisałem, ale ja uwzględniałem tylko pierwszego stopnia pochodną
19 sty 19:57
Mariusz: To też jest tylko pierwsza pochodna Jeśli na podstawie tego co napisałem napiszesz funkcję rekurencyjną to otrzymasz n. pochodną wielomianu
19 sty 20:43
Filip: Hmm, to potestuje to w kodzie i dam znać czy udało mi się napisać
20 sty 00:41
Mariusz: Widziałeś ten wzór jawny na wielomiany Czebyszowa ? Tutaj miałeś dwa warianty pierwszego podstawienia
20 sty 04:28
Filip: Hmm, jak ciekawie tutaj rekurencje zrobić struct Polynomial* ddx(struct Polynomial* p) { if (p−>deg==0) return p; struct Polynomial* dp=malloc(sizeof(*p)); dp−>deg=p−>deg−1; dp−>coeff=malloc(sizeof(dp−>deg+1)*sizeof(*dp−>coeff)); for (int i = 0; i < dp−>deg+1; i++) { dp−>coeff[i]=(i+1)*p−>coeff[i+1]; } ddx(dp); }
20 sty 18:03
Filip: nie wiem czy to będzie poprawne ze względu na malloci() w kolejnych wywołaniach rekurencyjnych
20 sty 18:03
Mariusz: Ja myślałem o czymś takim struct Polynomial* ddx(struct Polynomial* p, int k) { if(k == 0) return p; // Tutaj przypadek gdy k > 0 } Spróbuje się tym pobawić i przetestować Ty też możesz się tym pobawić
20 sty 18:21
Filip: racja, zapomniałem o tym współczynniku, wtedy wywołanie rekurencyjne by wygladało tak: (dp, n−1);
20 sty 18:23
Filip: właśnie idę, może spróbuje bez dodatkowe alokacji
20 sty 18:23
Filip: struct Polynomial* ddx(struct Polynomial* p, int n) { if (n==0) return p; p−>deg−=1; struct Polynomial* temp=malloc(sizeof(*temp)); temp−>deg=p−>deg; temp−>coeff=realloc(p−>coeff,(p−>deg+1)*sizeof(*p−>coeff)); for (int i=0;i<temp−>deg+1;i++) { temp−>coeff[i]=(i+1)*temp−>coeff[i+1]; } ddx(temp,n−1); } Narazie nie wpadłem, aby działał dla pochodnej większego stopnia niż 1, Działa tylko dla n=1 lub n=0
20 sty 19:11
Filip: struct Polynomial* ddx(struct Polynomial* p, int n) { if (n==0) return p; struct Polynomial* temp=malloc(sizeof(*temp)); p−>deg−=1; temp−>deg=p−>deg; temp−>coeff=malloc((p−>deg+1)*sizeof(*p−>coeff)); for (int i=0;i<temp−>deg+1;i++) { temp−>coeff[i]=(i+1)*p−>coeff[i+1]; } ddx(temp,n−1); } Poprawiona funkcja, powinno działać teraz
20 sty 19:25
Filip: Działa tylko dla Tn, gdzie n>=2
20 sty 19:28
Filip: Jednak, dla pochodnej stopnia przykładowo 5 nie działa nawet, więc myślę jak się przed tym zabezpieczyć Może dam ifa, jeśli stopień pochodnej większy od stopnia wielomianu, return 0
20 sty 19:32
Filip: struct Polynomial* ddx(struct Polynomial* p, int n) { if (n==0) return p; struct Polynomial* temp=malloc(sizeof(*temp)); if (n>p−>deg) { temp−>deg=0; temp−>coeff=malloc(sizeof(*temp−>coeff)); temp−>coeff[0]=0; return temp; } p−>deg−=1; temp−>deg=p−>deg; temp−>coeff=malloc((p−>deg+1)*sizeof(*p−>coeff)); for (int i=0;i<temp−>deg+1;i++) { temp−>coeff[i]=(i+1)*p−>coeff[i+1]; } ddx(temp,n−1); } Powinno działać
20 sty 19:35
Mariusz: A może dobrze by było sobie rozpisać jak ta rekurencja miałaby wyglądać, jak będzie wywoływana itp W pseudokodzie to chyba wyglądałoby tak struct Polynomial* ddx(struct Polynomial* p) Dla n == 0 zwracasz p W przeciwnym przypadku Przyjmujesz że funkcja policzyła ci ci już n−1 pochodną i liczysz pierwszą pochodną tego co ci zwróciła a pierwszą pochodną liczysz właśnie tak dp−>deg=p−>deg−1; dp−>coeff=malloc(sizeof(dp−>deg+1)*sizeof(*dp−>coeff)); for (int i = 0; i < dp−>deg+1; i++) { dp−>coeff[i]=(i+1)*p−>coeff[i+1]; } Ty zapisałeś tę rekurencję ogonowo więc gdyby zadziałała to stosunkowo łatwo byłoby ją zamienić na iterację
20 sty 19:36
Mariusz: A to nawet lepiej że przeniosłeś wywołanie rekurencyjna na koniec Łatwiej będzie tą rekurencję wyeliminować choć obecne komipiatory chyba automatycznie to optymalizują
20 sty 20:28
Filip: No dobra to to mam, a teraz jak obliczyć wartość pochodnej dla danego x?
20 sty 22:30
Mariusz: double Horner(struct Polynomial *p,double x) { double v = p−>coeff[0]; for(int i=1;i<= p−>deg;i++) v = v * x +p>coeff[i]; return v; } Może coś takiego Miałeś schemat Hornera na zajęciach ?
20 sty 23:03
Filip: yy miałem w liceum, jednak nie pamiętam go zupełnie (no jedynie że coś się brało współczynniki i to w jakieś tabelce zapisywaliśmy − na tabliczce woskowej), bo z niego ani razu nie skorzystałem
20 sty 23:07
Mariusz: Gdybyś podał jakiś przykład to mógłbym si przypomnieć jak go stosować (Służy do dzielenia wielomianu przez dwumian a reszta z tego dzielenia jest przy okazji wartością wielomianu w punkcie) A temat podstawieniami Eulera będziemy kontynuować ? Jeśli tak to myślę że możemy już do nich przejść chyba że chciałbyś jeszcze przećwiczyć metodę Ostrogradskiego
20 sty 23:48
Filip: właśnie to jc kiedyś pisał tutaj o tym, że przykładowo dla wielomianu ax3+bx2+cx+d nie liczymy w tradycyjny sposób, tylko: x(ax2+bx)+c x(x(ax+b)+c)+d czyli zaczynamy od: ax+b x(ax+b)+c x(x(ax+b)+c)+d Czy w kodzie to wygląda na dobrze zapisane: double result=p−>coeff[0]; for (int i=1;i<p−>deg+1;i++) result=result*x+p−>coeff[i]; return result; No przepisałem twój kod tak właściwie, tylko bez szablonu funkcji
20 sty 23:59
Filip: Tak, Eulera możemy zacząć od piątku
21 sty 00:01
Mariusz: Weźmy jako przykład wielomian Czebyszowa T3 i dwumian x − 1 (4x3−3x):(x−1) Zapisujesz współczynniki wielomianu w tabelce Z boku zapisujesz x0 z dwumianu (x−x0) (Tutaj x0=1) 4 0 −3 0 1 Teraz współczynnik przy x3 przepisujesz 4 0 −3 0 1 4 ^ | Mnożysz 1 przez 4 pokazaną przez tę "strzałkę" i dodajesz współczynnik przy x2 4 0 −3 0 1 4 4 ^ | Mnożysz 1 przez 4 pokazaną przez tę "strzałkę" i dodajesz współczynnik przy x 4 0 −3 0 1 4 4 1 ^ | Mnożysz 1 przez 1 pokazaną przez tę "strzałkę" i dodajesz współczynnik przy wyrazie wolnym 4 0 −3 0 1 4 4 1 1 Teraz odczytujesz wynik trzy pierwsze współczynniki to iloraz 4x2+4x+1 a ostatni to reszta i przy okazji także wartość wielomianu w punkcie x=1 No to teraz sobie to przećwicz Może uda ci się samemu napisać tę funkcję − to tylko jedna pętla
21 sty 00:16
Mariusz: Tutaj chyba będzie lepiej dać wsteczną iterację i zainicjować współczynnikiem wiodącym double Horner(struct Polynomial *p,double x) { double v = p−>coeff[p−>deg]; for(int i=p−>deg−1;i >= 0;i−−) v = v * x +p>coeff[i]; return v; }
21 sty 00:31
Mariusz: Tak ta druga wersja jest dobra
21 sty 00:38
Mariusz: Zasugerowałem się błędnym rozwiązaniem znalezionym w sieci ale jak rozpisałem dla ciebie przykład to już zacząłem podejrzewać że coś jest nie tak Poniższa wersja będzie dobra double Horner(struct Polynomial *p,double x) { double v = p−>coeff[p−>deg]; for(int i=p−>deg−1;i >= 0;i−−) v = v * x +p−>coeff[i]; return v; } Oczwiście moglibyśmy zainicjować zmienną v zerem ale mielibyśmy wtedy jedną interację więcej
21 sty 00:57
Filip: W sumie racja, tak teraz patrze, że jak napisałem przykład to wynika też z tego, że liczymy od największej do najmniejszej potęgi wykładnika
21 sty 14:42
Mariusz: Gdybyś trochę poczytał o schemacie Hornera i odpowiednio go zmodyfikował to okazałoby się że mógłby on zastąpić tę funkcję do liczenia pochodnej
21 sty 18:24
Filip: Aktulanie testuje napisaną aproksymacje funkcji właśnie za pomocą wielomianów Czebyszewa. Masz może pomysł, na jakiś wzór funkcji wyjściowej dla której wylicze punkty z przedziału [a,b] co (a+b)/m, gdzie m − liczba punktów, dla której aproksymacja może się nie powieść, albo będzie bardzo niedokładna?
21 sty 20:01
Mariusz: Ja akurat interpolacją wielomianami Czebyszowa się nie bawiłem Bawiłem się interpolacją Lagrange ale też niezbyt dużo
22 sty 03:45
Mariusz : Umiałbyś napisać wersję offline tego programu co podała Qulka https://matematykaszkolna.pl/forum/407266.html Na Windows preferowany język to C# Na Linux to C++ Na Androida mają już gotowca
31 sty 07:42
Filip: Aktualnie nie siedze w C++ i troche go już nie pamiętam
31 sty 17:41
Mariusz: Gdybyś miał dopisać do tej listy co napisaliśmy sortowanie przez łączenie naturalne to jak napisałbyś je
6 lut 21:52
Filip: Mam być szczerym, pierwszy raz słyszę o tym sortowaniu. Teraz mam wolne dwa tygodnie, także jutro mogę coś o nim poczytać i ew pytania na jakie nie znajdę odpowiedzi, mogę je zadać tutaj. Jak rozumiem nadal zostajemy przy języku C PS. Polecasz jakieś dobre źródło do nauki javy?
6 lut 22:07
Mariusz: Przeglądałem Thinking in Java a także Algorytmy w Javie Roberta Lafore Do C# nie mogę znaleźć dobrych książek choć już jakieś podstawowe programiki w nim pisałem − trochę przez podobieństwo do Javy Co do sortowania przez łączenie naturalne to oryginalnie było stosowane do sortowania plików ale można je zaadoptować do sortowania list
6 lut 23:12
Filip: Narazie napotkałem pierwszy problem, ze względu na to, iż nasza struktura wygląda tak struct Node { int val; struct Node* next; }; struct List { struct List* head; struct List* tail; } Przykładowo pokażę na przykładzie z wikipedi: Mamy taki zbiór danych: 1 5 2 3 8 10 4 2 7 5 8 9 1 I dzielimy go na posortowane serie (oddzielone znakiem '|') 1 5 | 2 3 8 10 | 4 | 2 7 | 5 8 9 | 1 Generalnie chyba nie wiemy, ile będziemy mieć takich serii, więc nie możemy robić oddzielnych list, może coś dodać do struktury List? struct List { struct List* head; struct List* tail; struct List* next; } I teraz nasze serie 1 5 | 2 3 8 10 | 4 | 2 7 | 5 8 9 | 1 Niech a = {1, 5} b = {2, 3, 8, 10} Zapisać tak: a−>head = 1 a−>tail = 5 a−>next = b; b−>head = 2 b−>tail = 10 b−>next = c{4} W sensie narazie nie testowałem czy będzie to miało sens, ale jest lepszy niż operowanie na x oddzielnych listach, bez możliwości efektywnego "przeskakiwania" między nimi
7 lut 15:20
Filip: A no i pierwszy krok, to muszę pomyśleć jak napisać funkcję, aby właśnie mi dzieliło np: 1 5 2 3 8 10 4 2 7 5 8 9 1 na takie serie 1 5 | 2 3 8 10 | 4 | 2 7 | 5 8 9 | 1
7 lut 15:23
Filip: aa, no i może tego nowego pola nie nazywać next, bo może wystąpić koalizja oznaczeń − już mamy w struct Node pole o nazwie next
7 lut 15:25
Mariusz: Nie zmieniasz struktury listy Korzystasz z dwóch dodatkowych list przy czym nie allokujesz pamięci na nowe węzły bo sortowanie listy polega na podmianie wskaźników Serie z sortowanej listy rozdzielasz na przemian do dwóch pomocniczych list
7 lut 15:57
Filip: Hmm, mógłbyś przybliżyć? 1 5 | 2 3 8 10 | 4 | 2 7 | 5 8 9 | 1 Lista A − 1 5 4 5 8 9 Lista B − 2 3 8 10 2 7 1 I co dalej z tym robie, bo tego nie widzę
7 lut 16:11
Mariusz: Teraz przy scalaniu bierzesz sobie dwie flagi które będą przechowywać informację czy skończyła się seria Pierwsza flaga jest ustawiana gdy skończy ci się aktualna seria na liście A wtedy kopiujesz pozostałe węzły z aktualnej serii na liście B , ustawiasz drugą flagę i rozpoczynasz łączenie następnych serii Łączenie kończysz gdy wyczerpiesz serie z obydwu list Procedury dzielenia i łączenia powtarzasz aż po etapie dzielenia lista B będzie pusta Przydatne będą trzy funkcje −rozdzielająca listę na dwie listy −łącząca listy −sortująca
7 lut 16:32
Mariusz: W tym przykładzie masz Przed fazą rozdzielania Lista L − 1 5 | 2 3 8 10 | 4 | 2 7 | 5 8 9 | 1 Lista A − NULL Lista B − NULL Po fazie rozdzielania Lista L − NULL Lista A − 1 5 | 4 5 8 9 Lista B − 2 3 8 10 | 2 7 | 1 Po fazie łączenia Lista A − NULL Lista B − NULL Lista L − 1 2 3 5 8 10 | 2 4 5 7 8 9 | 1
7 lut 16:55
Filip: Funkcje rozdzielającą napisałem tak: void splitList(struct List* L, struct List* A, struct List* B) { int where = 0; struct Node* temp = L−>head; while (temp != NULL) { double comp = temp−>val; if (where % 2 == 0) { while (comp <= temp−>val && temp != NULL) { addElement(A, temp); temp = temp−>next; comp = temp−>prev−>val; } } else { while (comp <= temp−>val && temp != NULL) { addElement(B, temp); temp = temp−>next; comp = temp−>prev−>val; } } where++; } } Chyba funkcje scalająca listy już pisaliśmy
7 lut 17:33
Filip: Nie, jednak nie pisaliśmy, daj znać co sądzisz o mojej implementacji − czy przede wszystkim jest poprawna (nawet w brzegowych warunkach)
7 lut 17:35
Filip: No właśnie, odnośnie fazy łączenia, zostajemy nadal przy tych samych danych z 16:55 Jak rozpoznam, do kiedy moge łączyć elementy moge łączyć elementy − w sensie Pierwsze łączenie a − {1, 5} −− skąd wiem, że jest tutaj koniec pierwszej serii? b − {2, 3, 8, 10} Zakładam, że będzie to mniej więcej szło tak: Wybieram serie, o mniejszej długości − w tym wypadku a int i = 0; int j = 0; dopóki pierwsza seria a się nie skończy && i < długość serii a jeżeli a[i] < b[j] addElement(L, a[i]) i++; else addElement(L, b[j]) j++; for (int k = j; j < długośc serii b; k++) addElement(L, b[k]); Tak w wielkim skrócie bym widział scalanie, jednak użyłem operatora [], aby dowiedzieć się czy dobrze zrozumiałem odnośnie zwykłych tablic (wiadomona liście będzie to wyglądać trochę inaczej, ale poglądowo chciałem zrobić)
7 lut 17:45
Filip: Łączenie*, nie scalanie
7 lut 17:45
Mariusz: przeglądasz listę dwoma wskaźnikami Pamiętasz wskaźnik na poprzednika i na aktualny węzeł Jeżeli poprzedni większy od aktualnego to znaczy że rozpoczęła się nowa seria
7 lut 18:34
Mariusz: To co pisaliśmy to było sortowanie przez podział a tam scalanie sprowadzało się do konkatenacji No ja mam inaczej napisane rozdzielanie ale możliwe że twoja wersja też będzie działać musiałbym to sprawdzić
7 lut 18:49
Filip: Tak, moja implementacja jest niepoprawna, muszę się nad tym zastanowić
7 lut 20:26
Mariusz: Wiesz co jak konstruujesz warunki to najpierw sprawdzaj czy wskaźnik jest różny od NULLa a dopiero porównuj wartości na które ten wskaźnik wskazuje wtedy będziesz miał pewność że nie otrzymasz segmentation fault z powodu dereferencji NULLa Zadaj sobie pytanie od której strony kompilator sprawdza wyrażenia logiczne Czy funkcję rozdzielającą nie dałoby się zaprojektować tak aby zmienna where przyjmowała tylko wartości 0 oraz 1 ? U mnie zmienna where przyjmuje tylko wartości zero i jeden no i mam jedną pętle while a w niej warunek if a tak to wygląda dość podobnie
7 lut 20:53
Filip: Poprawiłem funkcję, wygląda na to, że działa, ale możesz jeszcze potestować, tylko troche koszmarnie wygląda w kodzie − może da się to jakoś zrefaktoryzować void ListSplit(struct TList* L, struct TList* A, struct TList* B) { int where = 0; struct TNode* temp = L−>head; ListInit(A); ListInit(B); while (temp != NULL) { int comp = temp−>data; temp = temp−>next; struct TNode* n = malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; if (where % 2 == 0) { if (A−>head == NULL) { A−>head = n; } else { A−>tail−>next = n; n−>prev = A−>tail; } A−>tail = n; while (temp != NULL && comp <= temp−>data) { struct TNode* n = malloc(sizeof(*n)); n−>data = temp−>data; n−>next = NULL; n−>prev = NULL; A−>tail−>next = n; n−>prev = A−>tail; A−>tail = n; comp = temp−>data; temp = temp−>next; } } else { if (B−>head == NULL) { B−>head = n; } else { B−>tail−>next = n; n−>prev = B−>tail; } B−>tail = n; while (temp != NULL && comp <= temp−>data) { struct TNode* n = malloc(sizeof(*n)); n−>data = temp−>data; n−>next = NULL; n−>prev = NULL; B−>tail−>next = n; n−>prev = B−>tail; B−>tail = n; comp = temp−>data; temp = temp−>next; } } where++; } }
7 lut 20:55
Filip: ListInit − wziąłem z twojego kodu co kiedyś w C++ podsyłałeś: To fragment twojego kodu template<class T> DoublyLinkedList<T>:oublyLinkedList() { setFirst(NULL); setLast(NULL); } Ja to przerobiłem na oddzielną funkcję, tak ona wygląda void ListInit(struct List* L) { L−>head = NULL; L−>tail = NULL; }
7 lut 20:59
Filip: przy okazji, znalazłem twój kod z C i przekopiowałem, by potestować swoją funkcję, tutaj jest on https://pastebin.com/1ZcN5jku Znalazłem także wątek, co z Pytającym rozmawiałem chyba właśnie o tym sortowaniu: https://matematykaszkolna.pl/forum/387956.html
7 lut 21:01
Filip: Ad 7.02 20:53 Tak, właśnie na tym wpadłem, ze względu na to iż próbowałęm wydobyć wartość temp−>data, chociaż już temp był NULLem − przez to min. wywalało mi program
7 lut 21:03
Mariusz: Kiedyś na innym forum pisałem z jednym kolesiem o tym sortowaniu i to się "urodziło" z jego pseudokodu Koleś pomógł więcej niż Pytający Tutaj dałem kod przed poprawkami które sam wprowadziłem aby was przetestować tzn sprawdzić czy wskażecie miejsce w kodzie gdzie program się "wysypuje"
7 lut 21:30
Filip: Jutro postaram się ten co napisałem kod zoptymalizować/poprawić, bo podejrzewam, że może troche tego być, ew inaczej podejść do problemu
7 lut 23:26
Mariusz: Jak co to w C++ mam poprawiony kod ale spróbuj coś samemu wymyślić
7 lut 23:52
Filip: A powiedz mi, czy wykorzystałeś do tego jedną czy dwie pętle while()? Bo zastanawiam się aby to zrobić tylko przy użyciu jednej pętli
8 lut 00:48
Mariusz: Fazę rozdzielania mam zrealizowaną z użyciem jednej pętli while Fazę łączenia z użyciem dwóch pętli while (jedna pętla scala serie a druga całe listy)
8 lut 01:08
Filip: void ListSplit(struct TList* L, struct TList* A, struct TList* B) { int where = 0; int waschange = 1; struct TNode* temp = L−>head; ListInit(A); ListInit(B); double comp; struct TList* q = NULL; while (temp != NULL) { if (where % 2 == 0) { q = A; } else { q = B; } if (waschange == 1) { comp = temp−>data; temp = temp−>next; struct TNode* n = malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; if (q−>head == NULL) { q−>head = n; } else { q−>tail−>next = n; n−>prev = q−>tail; } q−>tail = n; } if (temp != NULL && comp <= temp−>data) { struct TNode* n = malloc(sizeof(*n)); n−>data = temp−>data; n−>next = NULL; n−>prev = NULL; q−>tail−>next = n; n−>prev = q−>tail; q−>tail = n; comp = temp−>data; waschange = 0; } else { waschange = 1; where++; continue; } temp = temp−>next; } }
8 lut 12:59
Filip: Przychodzę z takim kodem, możesz potestować, ale na moje działa ok, jest jedna pętla while() Tutaj sie prosi dla czytelności kodu, by dopisać kolejne metody − dodawanie elementu do listy, oraz tworzącą nowy element
8 lut 13:00
Mariusz: Wygląda ok choć rozwiązanie nie jest optymalne pamięciowo bo kopiujesz listę Po rozdzieleniu lista L mogłaby być pusta Skoro nie zwracasz informacji o tym czy po rozdzieleniu druga z list jest pusta to pamiętaj aby to sprawdzić w funkcji sortującej Co do dodatkowych funkcji to można je napisać Proponuję dopisać takie które operują na kolejce a co do funkcji tworzącej węzły to wedle twojego uznania jeśli uważasz że się przyda to ją napisz
8 lut 13:59
Filip: Tylko pytanie jak wyeeliminować to kopiowanie listy cały czas? Po prostu rozpisać przypadek dla list A oraz dla listy B ?
8 lut 14:03
Mariusz: Wykorzystując operacje na kolejce ( te co pisaliśmy podczas sortowania przez podział) wstawiasz węzeł zdjęty z listy L na początku do listy A Następnie wstawiasz kolejne węzły do listy A aż zajdzie przypadek prev−>data > curr−>data Gdy ten przypadek zajdzie zmieniasz listę do której będziesz wstawiał węzły Aby kontrolować to do której z list wstawiać węzły możesz wykorzystać jedną ze zmiennych typu int Te pomocnicze funkcje już pisaliśmy void enqueue(struct List *L,struct Node *x) { x−>next = NULL; if(L−>head == NULL) L−>head = x; else L−>tail−>next = x; x−>prev = L−>tail; L−>tail = x; } struct Node * dequeue(struct List *L) { if(L−>head == NULL) return NULL; struct Node *temp = L−>head; if(L−>head−>next == NULL) L−>tail = NULL; else L−>head−>next−>prev = NULL; L−>head = L−>head−>next; return temp; } Patrz wpis z 16 sty 2021 18:44
8 lut 14:31
Filip: Czy przypadkiem w dequeue() nie powinniśmy sciągać wierzchołka, ustawiając jego pola next i prev na NULL?
8 lut 17:47
Filip: Tak samo w funkcji enqueue, czy w else nie powinno byc: L−>tail−>next = x; x−>prev = L−>tail
8 lut 17:56
Filip: Tak wlasciwie nie ma to znaczenia tak aptrze teraz
8 lut 17:58
Filip: void ListSplit(struct TList* L, struct TList* A, struct TList* B) { int where = 0; int waschange = 1; struct TNode* temp = L−>head; ListInit(A); ListInit(B); double comp; while (temp != NULL) { if (where % 2 == 0) { if (waschange == 1) { comp = temp−>data; temp = temp−>next; struct TNode* n = (struct TNode*)malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; enqueue(A, n); } if (temp != NULL && comp <= temp−>data) { comp = temp−>data; struct TNode* n = (struct TNode*)malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; enqueue(A, n); waschange = 0; } else { waschange = 1; where++; continue; } temp = temp−>next; } else { if (waschange == 1) { comp = temp−>data; temp = temp−>next; struct TNode* n = (struct TNode*)malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; enqueue(B, n); } if (temp != NULL && comp <= temp−>data) { comp = temp−>data; struct TNode* n = (struct TNode*)malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; enqueue(B, n); waschange = 0; } else { waschange = 1; where++; continue; } temp = temp−>next; } } } Pozmieniałem, używając funkcji enqueue(), natomiast mam problem, by ten fragment kodu: struct TNode* n = (struct TNode*)malloc(sizeof(*n)); n−>data = comp; n−>next = NULL; n−>prev = NULL; Zastąpić funkcją dequeue() − program nie działa prawidłowo, gdy powyższy kod zapisałem jako: struct TNode* n = dequeue(L); PS. Przerzuciłem się na pracę do vscode'a, którego mam skonfigurowanego pod C++, dlatego przed mallociem() zacząłem używać (struct TNode*)
8 lut 18:38
Mariusz: void ListSplit(struct TList *L,struct TList *A, struct TList *B) { int comp; int wasChanged = 0; ListInit(A); ListInit(B); struct TNode *temp = dequeue(L); enqueue(A,temp); while(L−>head != NULL) { comp = temp−>data; temp = dequeue(L); if(comp > temp−>data) wasChanged = 1 − wasChanged; if(wasChanged) enqueue(B,temp); else enqueue(A,temp); } } A taka funkcja rozdzielająca może być ? Zmieniłem typ zmiennej comp na int bo taki u mnie był typ ze względu na uproszczone losowanie (losowanie dla double byłoby bardziej skomplikowane) Wg mnie zaproponowane przeze mnie rozwiązanie powinno działać i jest oszczędne pamięciowo bo nie kopiuje listy
8 lut 20:15
Mariusz: Dodaj jeszcze warunek przed pierwszym wstawianiem aby przypadek brzegowy ci się zgadzał
8 lut 20:25
Filip: No tak, ten twój kod dużo lepszy, jednak bym dodał pewną modyfikacje − jeden warunek [...] struct TNode* temp = dequeue(L); if (temp == NULL) return; [...] Co ty na taki warunek? W funkcji enqueue() nie sprawdzamy, czy temp == NULL, więc może by wypadało sprawdzić przed
8 lut 23:03
Filip: Teraz zobaczyłem, że mówiłeś coś o tym we wpisie z 8 lut 20:25 − czy chodziło ci o ten warunek co powyżej?
8 lut 23:04
Mariusz: Po tym jak zauważyłem że program nie będzie działał dla przypadku brzegowego to u siebie użyłem nieco innego warunku ale zaproponowany przez ciebie jest ok Ja u siebie mam negację zaproponowanego przez ciebie warunku ale zostań przy swojej propozycji bo też jest dobra
8 lut 23:17
Filip: Mogłbyś przypomnieć co robimy dalej? Bo mamy już ten etap sortowania napisany: L − 14 13 15 14 1 12 3 1 Po podzieleniu listy: A − 14 | 14 | 3 B − 13 15 | 1 12 | 1 L − NULL Robi to nasza funkcja ListSplit() Jak rozumiem teraz musimy napisać funkcję, która połączy nam to tak? L − 13 14 15 | 1 12 14 | 1 3 A − NULL B − NULL No i tak właściwie to co po tym dalej robimy?
8 lut 23:36
Mariusz: Powtarzasz w pętli fazę rozdzielania i łączenia aż do momentu gdy po wywołaniu funkcji rozdzielającej − u nas ListSplit() lista B będzie pusta Teraz trzeba się jednak skupić na napisaniu funkcji łączącej Listy łączysz seriami , łączenie następnych serii zaczynasz gdy skończą się aktualnie łączone serie Gdy skończy ci się seria na jednej z list wtedy pozostałe węzły z aktualnej serii wstawiasz do listy która będzie wynikiem łączenia
8 lut 23:53
Filip: No właśnie to jest ciekawe... jak rozpoznamy, że przykładowo w liście A skończyła się pierwsza seria? Myslałem nad warunkiem if (temp−>data >= temp−>next−>data) tylko pytanie czy tak będzie zawsze, nie mogę znaleźć kontrprzykładu do mojej tezy
9 lut 00:11
Filip: ok, jednak udało mi się znaleźć kontrprzykład, ten warunek będzie zły chociażby dla takiej listy L − 5 6 7 30 1 40 37
9 lut 00:13
Mariusz: Może dać dwie flagi w których będziesz przechowywał informację o końcu serii Ja pierwsze węzły z listy pobieram poza pętlą i wtedy aby wykryć koniec serii porównuję wartość klucza z aktualnego węzła z wartością klucza poprzednika Mam też bardziej rozbudowany warunek if do wykrywania końca serii niż ten co zaproponowałeś Spróbuj pomyśleć jak zrealizować łączenie aktualnych serii Jak już będziesz wiedział jak łączyć serie to łączenie całych list będzie już stosunkowo łatwe
9 lut 00:37
Mariusz: Po rozdzieleniu listy L nasz algorytm rozpozna tylko jedną serię na liście B Można spróbować zastanowić się jak zaprojektować algorytm aby poprawnie rozpoznawał liczbę serii ale nawet jeśli nasz algorytm rozpozna na liście B tylko jedną serię to możemy stracić co najwyżej stabilność sortowania
9 lut 00:52
Filip: "i wtedy aby wykryć koniec serii porównuję wartość klucza z aktualnego węzła z wartością klucza poprzednika" Właśnie nie wiem jak to ma zadziałać, kontrprzykład podałem we wpisie 9 lut 00:13, chyba że dodajesz jeszcze jakieś warunki poza tym jednym
9 lut 13:46
Mariusz: W tej instrukcji if mam warunek złożony z dwóch wyrażeń logicznych połączonych operatorem or tak aby nie wywalało segmentation fault podczas porównywania wartości klucza aktualnego węzła i wartości klucza poprzedniego węzła Tak porównując przedefiniujemy sobie serie − w twoim przykładzie na liście B Sprawdzę jak napisana przeze mnie funkcja łączy serie ale dla twojego przykładu najprawdopodobniej na liście B rozpozna ona tylko jedną serię
9 lut 14:20
Mariusz: Wcześniej samodzielnie źle rozdzieliłem węzły i myślałem że to na liście B będziemy mieli sytuację że funkcja łącząca rozpozna tylko jedną serię U mnie też wykrywa tylko jedną serię na liście A Dzieje się tak dlatego że w funkcji łączącej wyszukuje serie po raz wtóry i obydwie serie na liście A powstałe w wyniku działania funkcji rozdzielającej tworzą jedną serię i dlatego funkcja łącząca rozpoznaje tylko jedną serię na liście A Możemy zaakceptować to że funkcja łącząca po raz drugi wyszukuje serie albo poszukać sposobu na odtworzenie oryginalnych serii
9 lut 15:20
Filip: A tak właściwie, czy ma to jakieś duże znaczenie, jeśli serie rozpoznamy jako: A − 5 6 7 30 | 37 B − 1 40 Czy jako A − 5 6 7 30 37 B − 1 40 Można się zastanowić jak to wpłynie na ewentualnie łączenie i na całe sortowanie, ale nie wygląda na to, by to jakoś specjalnie obciążało. Więc może zostańmy przy tym, że jednym z warunków na wykrycie nowej serii będzie po prostu: if (temp−>next−>data <= temp−>data)
9 lut 16:04
Filip: Bo właśnie chodziło mi o zmodyfikowanie struktury Tlist, dodać wskaźnik na następną serię Wtedy jak masz takie serie: A − 5 6 7 | 13 15 | 2 B − 1 14 15 | 2 8 | 12 To będziesz dokładnie wiedział, kiedy kończy/zaczyna się seria − jednak wymaga to modyfikacji większej ilośći kodu Więc chyba zostaniemy przy tym co napisałeś oraz z wpisu 9 lut 16:04
9 lut 16:07
Mariusz: Już wcześniej pisałem że nie ma to większego znaczenia a jedyne co możemy stracić to stabilność sortowania czyli to że w przypadku węzłów z z takimi samymi wartościami kluczy ich kolejność na posortowanej liście może być różna niż na oryginalnej liście Sugerowałbym aby warunek do wykrywania końca serii skonstruować z użyciem operatora logicznego aby zapobiec błędowi segmentation fault
9 lut 16:15
Filip: No tak, temp−>next != NULL, dobra coś dzisiaj popisze i będę podsyłał
9 lut 16:18
Filip: Jak postępować w przypadku, gdy A lub B będą puste. Po prostu jak jedna lista jest pusta a druga nie jest pusta, to L = niepusta lista ?
9 lut 17:31
Filip: Tak właściwie to nie bylo pytania, tylko B może być puste, wtedy mamy w liście A juz posortowane wartości
9 lut 17:36
Filip: void MergeLists(struct TList* L, struct TList* A, struct TList* B) { if (B−>head == NULL) return; ListInit(L); struct TNode* tempA = dequeue(A); struct TNode* tempB = dequeue(B); struct TNode* valA = NULL; struct TNode* valB = NULL; int endA = 0; int endB = 0; while (A−>head != NULL || B−>head != NULL) { while (endA != 1 && endB != 1) { if (tempA−>data <= tempB−>data) { enqueue(L, tempA); valA = tempA; tempA = dequeue(A); if (tempA == NULL || tempA−>data < valA−>data) endA = 1; } else { enqueue(L, tempB); valB = tempB; tempB = dequeue(B); if (tempB == NULL || tempB−>data < valB−>data) endB = 1; } } if (endA == 1) printf("DIAS"); while (endA != 1) { enqueue(L, tempA); valA = tempA; tempA = dequeue(A); if (tempA == NULL || valA == NULL) endA = 1; else if (tempA−>data < valA−>data) endA = 1; } while (endB != 1) { enqueue(L, tempB); valB = tempB; tempB = dequeue(B); if (tempB == NULL || valB == NULL) endB = 1; else if (tempB−>data < valB−>data) endB = 1; } endA = 0; endB = 0; valA = NULL; valB = NULL; } } Podsyłam kod, który udało mi się napisać. Nie działa on jednak poprawnie − w sensie raz działa poprawnie, raz nie dodaje jednego elementu z serii do listy L, raz się wysypuje, może uda ci się znaleźć błąd, ja narazie go nie widzę
9 lut 20:43
Filip: Tę linijkę kodu if (endA == 1) printf("DIAS"); możesz ominąć − używałem jej do testów i zapomniałem usunąc
9 lut 21:00
Mariusz: Mój kod różni się w kilku miejscach while (A−>head != NULL || B−>head != NULL) Tutaj zamiast A−>head != NULL || B−>head != NULL mam tempA != NULL || tempB != NULL ponieważ zdjąłeś elementy z obydwu list ale ich nie wstawiłeś przed pętlą Tam gdzie ty masz endA = 0 endB = 0 ja mam endA = (tempA == NULL) endB = (tempB == NULL) Zastanawiam się czy to zwrócenie pustej instrukcji w przypadku gdy lista B jest pusta nie wpłynie jakoś na to sortowanie bo wg mnie w tym przypadku to na liście L powinny się znajdować węzły które przed wywołaniem funkcji były na liście A , lista A natomiast powinna być pusta
10 lut 02:43
Filip: Zaraz zobaczę te warunki co napisałeś, odnośnie tego if'a() Tak teraz widzę, że są dwie opcje, co umieścić w jego ciele (1) Przekopiować listę A do listy L i listę A zrobić pustą (2) tego if'a wstawić tuż po zadeklarowaniu zmiennych mówiących o końcu listy i zrobić tak: int endA = tempA == NULL ? 1 : 0; int endB = tempB == NULL ? 1 : 0; tak właściwie chyba tak powinniśmy deklarować te zmienne
10 lut 14:00
Mariusz: Tym operatorem trójargumentowym też można Ja w swoim kodzie w ogóle ten przypadek pominąłem ale ty możesz go wydzielić
10 lut 14:13
Filip: void MergeLists(struct TList* L, struct TList* A, struct TList* B) { ListInit(L); struct TNode* tempA = dequeue(A); struct TNode* tempB = dequeue(B); struct TNode* valA = NULL; struct TNode* valB = NULL; int endA = tempA == NULL ? 1 : 0; int endB = tempB == NULL ? 1 : 0; while (tempA != NULL || tempB != NULL) { while (endA != 1 && endB != 1) { if (tempA−>data <= tempB−>data) { enqueue(L, tempA); valA = tempA; tempA = dequeue(A); if (tempA == NULL || tempA−>data < valA−>data) endA = 1; } else { enqueue(L, tempB); valB = tempB; tempB = dequeue(B); if (tempB == NULL || tempB−>data < valB−>data) endB = 1; } } while (endA != 1) { enqueue(L, tempA); valA = tempA; tempA = dequeue(A); if (tempA == NULL || valA == NULL) endA = 1; else if (tempA−>data < valA−>data) endA = 1; } while (endB != 1) { enqueue(L, tempB); valB = tempB; tempB = dequeue(B); if (tempB == NULL || valB == NULL) endB = 1; else if (tempB−>data < valB−>data) endB = 1; } endA = tempA == NULL ? 1 : 0; endB = tempB == NULL ? 1 : 0; valA = NULL; valB = NULL; } } Po zaimplementowaniu twoich uwag, kod wygląda na to, że działa, troche potestowałem i ani razu się nie wysypał, oraz dobrze "posortował" odpowiednie serie. Jednak zastanawiam się, czy nie da się inaczej zapisać tego fragmentu kodu: if (tempA == NULL || valA == NULL) endA = 1; else if (tempA−>data < valA−>data) endA = 1; Przykładowo próbowałem usunąc z tego if'a warunek valA == NULL, ponieważ jeżeli wejdziemy do tej pętli while, valA nie będzie NULL'em, i przerobić cały warunek na taki: if (tempA == NULL || tempA−>data < valA−>data) endA = 1; Jednak przy takim warunku kod się wysypuje
10 lut 14:15
Mariusz: Pamiętasz na jakich danych ci się wysypuje bo ja właśnie taki warunek miałem i u mnie się nie wysypywał
10 lut 14:50
Filip: A nie, działa poprawnie, coś musiało mi się przewidzieć, albo co innego powodowało problemy
10 lut 17:46
Filip: No dobra, ten etap wygląda na zakończony, co dalej?
10 lut 17:51
Mariusz: Teraz wystarczy napisać funkcję sortującą Pętla do{}while() będzie nieco wygodniejsza w użyciu niż pętla while(){} Można użyć jednej z dwóch Wywołujesz w pętli funkcje rozdzielającą i łączącą dopóki po wywołaniu funkcji rozdzielającej lista B będzie pusta
10 lut 18:30
Mariusz: Funkcję sortującą już dość łatwo napisać Gdy już ją napiszesz można będzie zastanowić się nad usprawnieniem tego algorytmu np takim Gdybyśmy scalali do dwóch list zamiast do jednej to funkcję rozdzielającą można by było wywołać tylko raz Ten pomysł widziałem w książce Diksa i Ryttera 2.10.1 Scalanie wielofazowe z 4 plikami Załóżmy że bloki zostały rozłożone na pliki P0 i P1 Pliki P2 i P3 są początkowo puste i1 = 0; i2 = 1; // numery plików wejściowych , tzn otwartych do czytania j1 = 2; j2 = 3; // numery plików wyjściowych , tzn otwartych do pisania while jest wiecej niz jeden blok do (a) scal pierwszy blok na Pi1 z pierwszym blokiem na Pi2 i powstający blok zapisz na Pj1 (b) scal następny blok na Pi1 z następnym blokiem na Pi2 i powstający blok zapisz na Pj2 (c) powtarzaj kroki (a) i (b) (kładąc powstające bloki na przemian na pliki Pj1 i Pj2) aż zostanie osiągnięty koniec plików Pi1 i Pi2 (d) przewiń wszystkie pliki do początku; dodaj liczbę 2 mod 4 do i1,i2,j1,j2 odwracając w ten sposób rolę plików wejściowych
10 lut 21:31
Filip: Funkcję sortującą napisałem tak: void sortList(struct TList* L, struct TList* A, struct TList* B) { while (1) { ListSplit(L, A, B); if (B−>head == NULL) return; MergeLists(L, A, B); } } Posortowana lista będzie w argumencie drugim, czyli dla wywołania sortList(A, B, C) Posortowana lista będzie w B
10 lut 21:47
Filip: Tak właściwie można zmodyfikować ten kod na taki: void sortList(struct TList* L, struct TList* A, struct TList* B) { ListSplit(L, A, B); while (B−>head != NULL) { MergeLists(L, A, B); ListSplit(L, A, B); } } Jak by to wyglądało z pętlą do while?
10 lut 21:50
Filip: Wydaje mi się, że ta druga wersja będzie także poprawna Pytanie: Kiedyś widziałem podrzucałeś tutaj na forum jakieś zadanie, by napisać grę z kulkami i zbiciem(?). Pamiętasz może i jak znajdziesz podrzuciłbyś? Mam wolne do 20, a bym coś popisał w kodzie. Chyba, że masz jeszcze inne ciekawe problemy/implementacje do napisania
10 lut 21:52
Mariusz: Z do while to będzie wyglądało mniej więcej tak void ListSort(struct TList *L) { struct TList A; struct TList B; int isSorted; do { ListSplit(L,&A,&B); isSorted = (B.head == NULL); MergeLists(L,&A,&B); } while(!isSorted); } Jeśli chodzi o tę grę w kulki to kiedyś o niej pisałem gdy pisałem z filem o programowaniu Musiałbym poszukać bo nie pamiętam dokładnie numeru wątku Czy znasz C# ? Jest on jest dostarczany razem z Windowsem a także zdaje się można programować w nim grafikę bez doinstalowywania jakichś bibliotek Do C++ musiałbyś jakąś gotową bibliotekę graficzną doinstalować Spróbuje poszukać ten wątek w którym opisywałem założenia tej gry
10 lut 22:53
Mariusz: To ten wątek https://matematykaszkolna.pl/forum/400297.html Ciebie powinien zainteresować mój wpis z 28 kwietnia 14:01
10 lut 23:06
Filip: Właśnie na C# się nie znam, C++ trochę już nie pamiętam, myślałem aby sobie pójść bardziej w competetive programming, zacząć na stronie codeforces, oczywiście od trywialnych problemów i przechodzić coraz wyżej
11 lut 12:16
Mariusz: Jeśli chodzi o mnie to ja w szkole miałem Javę a w C# to jak na razie jakieś proste programy w nim pisałem Co do tych kulek to do C++ musiałbyś doinstalować jakąś bibliotekę graficzną Tutaj też można by się zastanowić jaką bibliotekę wybrać dobrze by było z obsługą myszy tak jak np SFML ale może znajdziesz coś lepszego Patrzyłeś na założenia tej gry które wypisałem w tamtym wątku ? Masz jakiś pomysł na jej napisanie ?
11 lut 13:21
Filip: Przypomina mi to, aby w tablicy sprawdzić, czy jest wolna droga z punktu A do B. Kiedyś pisałem przejście labiryntu tzn Szukanie drogi z A do B w labiryncie, tutaj podobnie będzie, tylko naszymi ścianami będą kulki − no i ew koniec planszy Jednak na jakiej zasadzie działa zbicie?
11 lut 13:52
Filip: bool solveMaze(int x, int y, std::vector<std::vector<char>>& maze) { if (maze[x][y] == 'B') return true; if (x < 0 || x >= maze.size() || y < 0 || y >= maze.size() || maze[x][y] != ' ') return false; maze[x][y]; if (solveMaze(x, y − 1, maze)) return true; if (solveMaze(x + 1, y, maze)) return true; if (solveMaze(x, y + 1, maze)) return true; if (solveMaze(x − 1, y, maze)) return true; maze[x][y] = ' '; return false; } Kiedyś pisałem taki kod w C++, szuka on dowolnej drogi między dwoma punktami (A, B) w labiryncie, myśle że tutaj ten kod można wykrozystać, jednak trzeba go przerobić z C++ na C
11 lut 14:04
Filip: Tam mi zjadło, zamiast maze[x][y]; powinno być maze[x][y] = '.'; //to jest oznaczenie drogi, mozna wybrac dowolny symbol
11 lut 14:10
Mariusz: No tak jeśli będzie wolna droga oraz pole nie będzie zajęte to kulkę można przenieść z pola A do pola B Zbicie − sprawdzasz czy masz co najmniej pięć kulek tego samego koloru obok siebie w pionie , w poziomie bądź na jednej z dwóch diagonali Jeżeli znajdziesz takie kulki to je usuwasz a następnie zwiększasz licznik punktów Tutaj naszą planszą będzie tablica 9x9 np int board[9][9] − 0 to puste pole − niezerowa wartość to numer koloru
11 lut 14:21
Filip: Moge tak w zasadzie napisać taki prototyb tej gry, w sensie bez tego "okienka", tylko przykładowo w konsolii gracz będzie podawać pole (x, y), na które chce posłać kulkę
11 lut 16:57
Filip: Tutaj narazie widzę 3 funkcje − na pewno sprawdzająca czy nastąpiło zbicie, przemieszczająca kulkę, oraz rozstawiająca kulki na planszy
11 lut 17:00
Filip: A kiedy gra się kończy?
11 lut 17:02
Mariusz: Gdy zapełnimy całą planszę i nie będziemy mieć wolnego pola aby wylosować kulkę Na początku losujemy pięć kulek (liczba kolorów uzależniona od poziomu trudności) a później w każdy a następnie po każdym ruchu który nie jest zakończony zbiciem losowane są trzy kulki W sumie w wersji konsolowej zamiast te kolorowe kulki mogą być reprezentowane przez jakieś znaki Poziom trudności gry to np liczba użytych znaków
11 lut 17:18
Filip: Hmm no dobra, czyli jak rozumiem będzie to wyglądać tak: 1. Losujemy kulkę (x5), którą reprezentują dwie liczby: a ∊ [0, 80] − położenie kulki na planszy b ∊ [1, liczbaKulek] − "kolor" kulki i umieszczamy na planszy 2. Gracz może przesunąć kulkę na dowolne − wolne pole na planszy, gdy jest do niego "droga" Teraz sprawdzamy czy nastąpiło bicie jeśli nastąpiło to usuwamy zbite kulki jeśli nie to losujemy kolejne 3 kulki i umieszczamy na planszy sprawdzamy, czy cała plansza jest zapełniona jeśli tak − koniec gry jeśli nie − wracamy do punktu 2
11 lut 18:37
Mariusz: No w sumie ja bym tak widział tę grę A i przydałby się jakiś sposób na ominięcie losowania już zajętego pola Może jest jakiś lepszy pomysł na to niż powtórzenie losowania
11 lut 19:23
Filip: Hmm, chyba zaczne od przerzucenia kodu z 11 lut 14:04 na C, i zobacze jakby to działało na planszy z kulkami
11 lut 19:33
Filip: No właśnie nie wiem czy się da bez powtórzenia losowania tak szczerze, chyba losowanie będzie zrealizowane mniej więcej tak: void draw(int n, int a, int b) { for (int i = 0; i < n; i++) { while (...) {} } }
11 lut 19:35
Filip: skoro plansza ma wymiary 9x9, to czy polozenie kulki nie powinismy losowac z przedzialu [0,89]?
12 lut 00:20
Filip: nawet [0,88]
12 lut 00:23
Mariusz: A czemu tak z przedziału [0,80] powinno wystarczyć Do czego potrzebujesz tych dodatkowych liczb ?
12 lut 01:19
Filip: Skoro mamy planszę 9x9, to jak wylosujemy 88 − [8][8] 81 − [8][1] Gdybysmy losowali tylko do 80, nie byloby nic w ostatnim wierszu od 1 indeksu do 8
12 lut 01:26
Mariusz: Niech a∊[0,80] wtedy indeksy mogłyby wyglądać tak [a/9][a%9]
12 lut 09:28
Filip: Racja, ja myślałem, gdybyśmy losowali z przedziału a ∊ [0, 88] grid[a / 10][a % 10]
12 lut 11:38
Filip: Losowanie napisałem tak: void draw(int n, int grid[9][9]) { for (int i = 0; i < n; i++) { int place = rand % 81; int colour = 1 + rand % n; //n − liczba kulek while (grid[place / 9][place % 9] != 0) { place = rand % 81; } grid[place / 9][place % 9] = colour; } }
12 lut 11:44
Filip: Może zamiast wszędzie pisać 9 − wprowadzić zmienną globalną? #define gridSize 9
12 lut 11:47
Mariusz: Tak to dobry pomysł z tą zmienną globalną Jeśli chodzi o tę funkcję losującą to może powinna ona przyjmować trzy parametry − liczba losowań − liczba kulek − plansza
12 lut 12:26
Mariusz: #define gridSize 9 void draw(int n,int m,int grid[gridSize][gridSize]) { for(int i = 0; i < n; i++) { int place = rand()% gridSize*gridSize; int colour = 1 + rand() % m // m − liczba kulek while(grid[place / gridSize][place % gridSize] != 0) place = rand()% gridSize*gridSize; grid[place / gridSize][place % gridSize] = colour; } }
12 lut 12:55
Mariusz: W sumie tak sobie pomyślałem że funkcja losująca mogłaby zwracać informację czy losowanie się powiodło tzn czy mamy miejsce na kulkę Jest też taka opcja aby ograniczyć funkcję draw do losowania tylko jednej kulki wtedy pętla for byłaby wykonywana poza funkcją draw
12 lut 13:03
jc: Wprowadź zmienną pamiętającą aktualną liczbę wolnych pól (nie to będzie n). Losuj liczbę z przedziału [1,2,...,n]. Niech to będzie k. Zaznacz k−te wolne pole (możesz iść po kolei, omijając pola zajęte).
12 lut 13:18
Filip: Czy liczba kulek nie będzie równa liczba losowań? Jeśli tak, trzeci argument wydaje się być zbędny
12 lut 13:45
Filip: trzeci jako liczba kulek − nie trzeci w kolejności od lewej do prawej
12 lut 13:46
Mariusz: No racja umieszczanie wylosowanej liczby będzie równie wolne jak dla listy ale powinno działać i chyba coś podobnego robiłem dla symulacji gry w lotto
12 lut 13:48
Mariusz: Nie, liczba kulek zależy od wybranego poziomu trudności i zwykle różni się od liczby losowań Rozważ też propozycję jc na zrealizowanie losowania
12 lut 13:51
jc: Przy kolejnych losowaniach liczba zajętych pól będzie rosła, ale po wykonaniu ruchu, liczba wolnych pól może wzrosnąć. Czy dopuszczasz losowanie, którego wynikiem będzie redukcja zajętych pól? Ja akurat wolę taką wersję.
12 lut 13:54
Mariusz: Liczba wolnych pól wzrośnie gdy nastąpi zbicie wtedy liczby o tej samej wartości które tworzą linię (w pionie , poziomie i na jednej z dwóch diagonali) zostają usunięte Linię tworzy co najmniej pięć liczb o tej samej wartości stojące obok siebie W sumie do sprawdzania czy liczby tworzą linię oraz do usuwania linii chyba lepiej napisać oddzielne funkcje
12 lut 14:11
Filip: Nawet kilka, można też rozbić funkcje sprawdzające, byśmy łącznie wtedy mieli 6 funkcji (1) checkRows() (2) checkCols() (3) checkDiagonal() (4) deleteRows() (5) deleteCols() (6) deleteDiagonal() No i w sumie jak dobrze myślę to jeszcze trzy funkcje sprawdzające czy jest zbicie w wierszy/kolumnie/na diagonali, zwracające 1 gdy jest − 0 gdy nie ma Tutaj ewentualnie będzie można się trochę pobawić i zrobić coś takiego void deleteRows(int (*checkRows)(int[][gridSize]), int grid[gridSize][gridSize]) { if ((checkRows)(grid) == 1) { ... } } jednak jest to już pewne kombinowanie, i nie wiem czy nie warto by zrobić tego if'a poza funkcją służącą do usuwania Co do propozycji jc, w kodzie bym to widział tak: // jedno losowanie int free = gridSize * gridSize; int place = rand() % free; int colour = 1+ rand() % m; // m − liczba kulek int temp = 0; int i = 0; while (temp != place) { if (grid[i / 9][i % 9] == 0) temp +=1; i++ } grid[temp / 9][temp % 9] = colour; Możecie dać znać czy poprawnie i co ew tutaj można poprawić
12 lut 14:52
Mariusz: Jeżeli chodzi o free to będzie kolizja nazw Po wylosowaniu zmniejszasz liczbę wolnych pól Po zbiciu musisz pamiętać o jej zwiększeniu o liczbę kulek które usunąłeś Tutaj zakładasz że liczba wolnych pól jest większa od zera
12 lut 15:11
Mariusz: Ja losowanie zaproponowane przez jc tak bym widział #define gridSize 9 void draw(int *n,int m,int grid[gridSize][gridSize]) { // m − liczba kulek // n − liczba wolnych pól // k − liczba odpowiadająca za pozycję losowanej kulki na planszy if(*n == 0) return; int counter=0; int k = rand()%(*n); int colour = 1 + rand()%m; while(counter != k) { if(grid[counter/9][counter%9] == 0) counter++; } grid[counter/9][counter%9] = colour; *n = *n−−; }
12 lut 15:32
Filip: Mhm, racja, jednak ja założyłem, że pierw będę miał funkjcę, która sprawdza czy ma nastąpić koniec gry czy nie − dlatego już nie sprawdzam tego warunku: if (*n == 0) No tak, u mnie jednak zmienna jest zbędna i nie zmniejszam ilości wolnych pól
12 lut 15:52
Mariusz: Jeśli chodzi o wyszukiwanie drogi to ja na początku myślałem nad jakimś algorytmem grafowym np wyszukiwanie najkrótszej ścieżki w grafie ale chyba ty znalazłeś lepszy sposób
12 lut 16:20
Filip: Może ustalmy jakiś schemat postępowania, bo trochę chaotycznie idziemy Na razie mamy funkcję losująca, którą umieściłbym tak: int main() { [...] int n = gridSize * gridSize; int m = 5; // losujemy 5 kulek na początku for (int i = 0; i < 5; i++) draw(&n, 5, grid); } //główna pętla gry while (checkEnd(gridSize * gridSize, grid) != 1) { // tutaj następuje ruch na wolne pole, więc (załóżmy narazie, że gracz podaje (x, y) na które chce przesunąc kule o współrzędnych (xp, yp) while (canMove(xp, yp, x, y, grid) != 1) // przesuwamy // teraz sprawdzamy czy jest bicie [...] // jeśli tak [P[jesli jest zbicie na wierszu/kolumnie/diagonali − usuwamy // losujemy 3 kulki i umieszczamy je na planszy m = 3; for (int i = 0; i < m; i++) draw(&n, m, grid); } Ja bym to mniej więcej tak widział, daj znąc czy to ma tak wyglądać, funkcje losującą wygląda na to że mamy, trzeba tylko potestowąc funkcje checkEnd() bym napisał tak; int checkEnd(int n, int grid[gridSize][gridSize]) { for (int i = 0; i < n; i++) if (grid[i / 9][i % 9] == 0) return 0; return 1; }
12 lut 16:40
Filip: Inaczej, powinniśmy zwracać informacje, czy umieszczanie się udało, przykładowo gdy losujemy trzy kulki, na planszy może być wolne 2 miejsca, więc położymy 2 i koniec gry
12 lut 16:44
Mariusz: Zmienna pamiętająca liczbę liczbę wolnych pól zawiera informację czy należy skończyć grę liczbaWolnychPól == 0 − koniec gry Tak jest po każdym wylosowaniu kulki sprawdzamy czy nie nastąpiło zbicie Pamiętaj że diagonalę sprawdzasz w dwie strony
12 lut 16:59
Mariusz: int main() { [...] int n = gridSize * gridSize; int m = 7; // liczba kulek od niej zależy poziom trudności gry, // w wersjach które widziałem dajemy graczowi możliwość wyboru jednej z trzech liczb // losujemy 5 kulek na początku for (int i = 0; i < 5; i++) draw(&n, 5, grid); } //główna pętla gry // Tutaj zmienna przechowująca liczbę wolnych pól zawiera informację czy skończyć grę while ( n != 0) { // tutaj następuje ruch na wolne pole, więc (załóżmy na razie, że gracz podaje (x, y) na które chce przesunąć kule o współrzędnych (xp, yp) while (canMove(xp, yp, x, y, grid) != 1) // przesuwamy // teraz sprawdzamy czy jest bicie [...] // jeśli tak [P[jesli jest zbicie na wierszu/kolumnie/diagonali − usuwamy // losujemy 3 kulki i umieszczamy je na planszy for (int i = 0; i < 3; i++) draw(&n, m, grid); } No na razie coś takiego mamy
12 lut 17:24
Mariusz: draw(&n, 5, grid); Tutaj mamy oczywiście draw(&n, m, grid); bo m jest liczbą kulek poza tym przy warunku dla pętli for dajemy n > 0 i w głównej pętli też możemy dać n > 0
12 lut 17:29
jc: Lata temu napisałem sobie coś takiego w javie. Losowanie działa tak, jak zasugerowałem. private int kulka (int k) { int m = 1 + Math.abs(p.nextInt ()) % n; n −−; for (int x = 1; x <= 9; x++) for (int y = 1; y <= 9; y++) { if ( c[x][y] == 0) m −−; if (m == 0) { c[x][y] = k; pole (x,y,k); return zmiana (x,y); } } return 0; } Dwie funkcje wewnątrz biorą się stąd, że dopuszczam, iż wylosowana kulka uzupełni jakiś układ do układu redukowalnego. n jest zmienną globalną oznaczającą liczbę wolnych miejsc.
12 lut 20:23
Filip: Tak właściwie to nie wiem, czy nie lepiej trzymać wymiary planszy w dwóch różnych zmiennych, ze względu na to, iż po usunięciu przykładowo całego wiersza, zmieni się tylko wartość wierszy a kolumny nadal pozostaną takie same: 9x9 po usunięciu dowolnego wiersza 8x9
12 lut 22:24
Mariusz: Podczas usuwania kulek zerujesz wartości tablicy a plansza nadal zostaje rozmiaru 9x9
12 lut 22:51
Filip: W funkcji losującej, zamiast: *n = *n−−; Powinno być *n = −−*n; Natomiast nie wiem czemu, funkcja ta nie działa w pętli, w sensie pojedyńcze wywołanie działa, natomiast np pięciokrotne już nie
12 lut 22:53
Filip: Mariusz wydaje mi się, że twoja funkcja jest niepoprawna, gdyż jeśli staniemy na elemencie niezerowym, program będzie leciał w nieskończoność
12 lut 22:59
Filip: Pozostanę przy takiej wersji, które wydaje się działać void draw(int* n, int m, int grid[gridSize][gridSize]) { if (*n == 0) return; int place = rand() % (*n); int colour = 1 + rand() % m; int temp = 0; int i = 0; while (temp != place) { if (grid[i / 9][i % 9] == 0) temp++; i++; } grid[temp / 9][temp % 9] = colour; *n = −−*n; } Output (dla pierwszego losowania 5 kulek): Przed losowaniem: 81 [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] Po losowaniu: 76 [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 7 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 1] [0 0 5 0 0 0 0 0 0] [0 0 0 0 0 0 0 3 0]
12 lut 23:07
Filip: Niestety nie mam też pewności czy ten kod co podałem odnośnie szukania drogi działa poprawnie − przełożyłem go na język C, jednak nie działa on poprawnie
12 lut 23:38
Mariusz: Problem może być gdy z losowania dostaniesz zero wtedy funkcja nie wejdzie ci do pętli i wtedy nadpiszesz pierwszy element
12 lut 23:38
Mariusz: A przeglądałeś jakieś algorytmy grafowe ? Może jakieś wyszukiwanie najkrótszych ścieżek w grafie by tutaj zadziałało
12 lut 23:40
Filip: Pisałem kiedyś dfs'a w grafie ważonym dla listy sąsiedztwa aby szukał najkrótszej drogi z wierzchołka A do B, może to coś pomoże
12 lut 23:48
Filip: Jeszcze sie zastanowie co moze byc nie tak z moja funkcja szukajaca drogi
12 lut 23:55
Filip: Przede wszystkim wytestuje czy dziala na normalnych labiryntach
12 lut 23:56
Mariusz: Funkcja losująca nie działa też poprawnie bo problem będzie gdy wylosujemy zero a grid[0][0] ≠ 0 to nie wejdzie nam do pętli i nadpisze nam wartość elementu grid[0][0] Wersja losowania napisana przez jc po poprawieniu indeksów będzie działać poprawnie tyle że wymaga wprowadzenia zmiennej globalnej
13 lut 00:08
Filip: A nie dałoby się tego obejść, inicjalizując zmienną temp wartością −1? I później do indeksowania pomyśleć nad kombinacją zmiennych temp/i oraz 1?
13 lut 00:16
Filip: Dlaczego przy rozwiązaniu jc musimy mieć zmienną globalną?
13 lut 00:19
Mariusz: No w sumie w rozwiązaniu jc też nie musimy mieć zmiennej globalnej On swój program pisał w Javie a tam nie ma wskaźników ani referencji i dał zmienną globalną
13 lut 00:35
Mariusz: Co do losowania to może takie coś void draw(int* n, int m, int grid[gridSize][gridSize]) { if (*n == 0) return; int place = rand() % (*n); int colour = 1 + rand() % m; int temp = 0; int i = 0; while(grid[temp /9][temp%9] != 0) temp++; while (place != 0 && temp != place) { if (grid[i / 9][i % 9] == 0) temp++; i++; } grid[temp / 9][temp % 9] = colour; *n = −−*n; } Oczywiście można też skorzystać z pomysłu jc
13 lut 01:19
Filip: Na szybko skleiłem kod w C++ #include <iostream> #include <vector> #include <fstream> bool solveMaze(int x, int y, std::vector<std::vector<char>>& maze) { if (maze[x][y] == 'B') return true; if (x < 0 || x >= maze.size() || y < 0 || y >= maze.size() || maze[x][y] != ' ') return false; maze[x][y] = '.'; if (solveMaze(x, y − 1, maze)) return true; if (solveMaze(x + 1, y, maze)) return true; if (solveMaze(x, y + 1, maze)) return true; if (solveMaze(x − 1, y, maze)) return true; maze[x][y] = ' '; return false; } int main() { std::ifstream file("a.txt"); std::string s; std::vector<std::vector<char>> board; int i = 0; while (std::getline(file, s)) { board.emplaceback(); for (auto c : s) board[i].pushback(c); i++; } board[0][1] = ' '; solveMaze(0, 1, board); board[0][1] = 'A'; for (int i = 0; i < board.size(); i++) { for (int j = 0; j < board[0].size(); j++) { std::cout << board[i][j]; } std::cout << '\n'; } } Możesz się pobawić, jednak algorytm działa poprawnie, przykładowo znajduje droge bardzo dobrze: Output: (1) #A####### #. ### ## #..### ## ##.# # # ##. . # ###. ### # ###. ##### #. . . ### #. ### ### #B####### (2) #A####### #. ### ## #. . ### ## ##. # # # ##. . .B # ### ### # ### ##### # ### # ### ### ######## Czyli poprawnie znajudje droge, może coś zepsułem w przepisywaniu na C emotka
13 lut 01:39
Filip: Znalazłem rozwiązanie, jednak nie wiem czy ci się spodoba, podeśle ci rano, bo mam je troche rozwalone w kodzie (w sensie brzydki kod), po krótce: Jak widzisz te 9 (uznalem ze bedzie to liczba > m aby bylo bezpiecznie, to jest droga, mozna to zniwelowac − po wykonaniu funkcji clear() Przed ruchem: 81 [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 5] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 5 0 0 0 0 0 0] [0 0 0 0 0 4 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 7 0] 2 8 8 8 // z pozycji (2, 8), przenosze na pozycje (8,8) Po ruchu: 76 [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [9 9 9 9 9 9 9 9 9] [9 9 9 9 9 9 9 9 9] [9 9 9 9 9 9 9 9 9] [9 9 5 0 0 0 0 0 0] [9 9 0 0 0 4 0 0 0] [9 9 9 9 9 9 9 9 9] [0 0 0 0 0 0 0 7 5] Po wyczyszczenie naszej drogi (uzyciu funkcji clear()) [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 5 0 0 0 0 0 0] [0 0 0 0 0 4 0 0 0] [0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 7 5] Czyli jak widac udalo sie przeniesc
13 lut 02:05
Filip: Tak wygląda fragment programu, oraz funkcji main() int canMove(int xStart, int yStart, int a, int grid[gridSize][gridSize]) { if (grid[xStart][yStart] == a) return 1; if (xStart < 0 || xStart >= gridSize || yStart < 0 || yStart >= gridSize || grid[xStart][yStart] != 0) return 0; grid[xStart][yStart] = a; if (canMove(xStart, yStart − 1,a, grid) == 1) return 1; if (canMove(xStart, yStart + 1, a, grid) == 1) return 1; if (canMove(xStart − 1, yStart, a, grid) == 1) return 1; if (canMove(xStart + 1, yStart, a, grid) == 1) return 1; grid[xStart][yStart] = 0; return 0; } void clear(int a, int grid[gridSize][gridSize]) { for (int i = 0; i < gridSize * gridSize; i++) if (grid[i / 9][i % 9] == a) grid[i / 9][i % 9] = 0; } int main() { [...] int xs, ys, xe, ye; scanf("%d %d %d %d", &xs, &ys, &xe, &ye); int a = grid[xs][ys]; grid[xs][ys] = 0; if (grid[xe][ye] == 0) { grid[xe][ye] = m + 3; if (canMove(xs, ys, m + 3, grid) == 1) grid[xe][ye] = a; else printf("brak drogi"); } else { printf("zly ruch"); } printf("\n\nPo ruchu: %d\n", n); clear(m + 3, grid); printGrid(grid); [...] }
13 lut 12:53
Filip: Tak patrze, nie jest to do końca poprawne, bo gdy nie ma drogi/zły ruch powinnienem na końcu nadpisywać grid[xs][ys] = a; Generalnie algorytm działa wtedy, gdy zaczniemy od pola o wartości 0 − dlatego podmieniam grid[xs][ys], oraz gdy na grid[xe][ye] mamy wartość unikalną (tutaj wybrałem m + 3, gdyż taka wartość będzie tylko jedna na planszy)
13 lut 12:55
Filip: No i oczywiście program się wcześniej gubił, ponieważ nie zaznaczałem aktualnej drogi, przez co, chodził można by powiedzieć, że w kółko, dlatego zaznaczam i później usuwam tę wartości w funkcji clear()
13 lut 12:58
Filip: Funkcję printGrid() napisałem tak: void printGrid(int grid[gridSize][gridSize]) { for (int i = 0; i < gridSize * gridSize; i++) { if (i % 9 == 0) printf("[%d ", grid[i / 9][i % 9]); else if (i == (i / 9 + 1) * 8 + i / 9) printf("%d]\n", grid[i / 9][i % 9]); else printf("%d ", grid[i / 9][i % 9]); } }
13 lut 16:35
Filip: Zła funkcja, jak dla mnie plansza będzie bardziej czytelna gdy będzie wyświetlana taką funkcją: void printGrid(int grid[gridSize][gridSize]) { printf(" "); for (int j = 0; j < gridSize; j++) printf("%d ", j + 1); printf("\n"); printf("\n"); for (int i = 0; i < gridSize * gridSize; i++) { if (i % 9 == 0) printf("%d %d ", i / 9 + 1, grid[i / 9][i % 9]); else if (i == (i / 9 + 1) * 8 + i / 9) printf("%d\n", grid[i / 9][i % 9]); else printf("%d ", grid[i / 9][i % 9]); } } Output: 1 2 3 4 5 6 7 8 9 1 6 3 0 2 0 0 6 3 6 2 0 1 0 1 1 4 0 4 7 3 0 0 1 0 3 1 4 5 5 4 2 0 5 6 3 7 5 7 0 5 4 3 0 3 4 0 0 5 7 6 1 0 3 6 0 3 0 0 3 7 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 4 0 9 0 0 0 0 0 0 0 0 0
13 lut 16:45
Filip: A, plus dwa błędy − funkcja szukająca drogi zwraca 1 w przypadku, gdy nie ma drogi − jakiś błąd jest + program nie zawsze działa, czasem na losowaniu się już wysypuje
13 lut 16:56
Filip: Dobra, szukanie drogi naprawiłem i teraz działa, pozostało to losowanie
13 lut 19:37
Mariusz: void draw(int* n, int m, int grid[gridSize][gridSize]) { if (*n == 0) return; int place = rand() % (*n); int colour = 1 + rand() % m; int temp = 0; int i = 0; while(place == 0 && grid[temp /9][temp%9] != 0) temp++; while (place != 0 && temp != place) { if (grid[i / 9][i % 9] == 0) temp++; i++; } grid[temp / 9][temp % 9] = colour; *n = −−*n; } Sprawdź tę funkcję losującą Ostatecznie funkcję losującą można wziąć od jc , odrobinę ją modyfikując
13 lut 19:41
Filip: Tak, ta działa
13 lut 22:35
Filip: To w takim razie jak działa zbicie, jest to 5 jednakowych kulek w wierszu/kolumnie/diagonali? Gdy nastąpi takie zbicie to zerujemy cały wiersz/kolumne/diagonale? (nawet inne, różne kulki od zbitych?) Czy takie coś też nazwiemy zbiciem? 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0
14 lut 12:11
Mariusz: Zerujemy tylko te które tworzą linię w wierszu , kolumnie , na diagonali , inne kulki zostawiamy (przy czym sprawdzasz obydwie diagonale) To może być co najmniej pięć kulek ale tych samych Tak takie coś co pokazałeś wymaga zbicia
14 lut 12:39
Mariusz: Jak masz 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 To zerujesz jedynki na tej diagonali tylko w pierwszych pięciu wierszach Jeśli natomiast miałbyś 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 To zerujesz jedynki na diagonali tylko w pierwszych sześciu wierszach
14 lut 13:16
Filip: Napisałem do sprawdzania na razie wierszy, nie wiem czy jest to optymalne, ale działa poprawnie: int checkCurrentRow(int row, int grid[gridSize][gridSize]) { for (int i = 0; i < 5; i++) { int check = 1; for (int j = 0; j < 4; j++) if (grid[row][i + j] == 0 || grid[row][i + j] != grid[row][i + j + 1]) check = 0; if (check == 1) return i; } return −1; } void checkRows(int* n, int grid[gridSize][gridSize]) { for (int i = 0; i < gridSize; i++) { int index = checkCurrentRow(i, grid); int ball = grid[i][index]; if (index != −1) while (index < gridSize && ball == grid[i][index]) { grid[i][index++] = 0; *n = −−*n; } } }
14 lut 13:26
Filip: Odnośnie kolumn analogiczne, tylko zamieniamy miejsce w tablicy na które będziemy patrzeć int checkCurrentColumn(int column, int grid[gridSize][gridSize]) { for (int i = 0; i < 5; i++) { int check = 1; for (int j = 0; j < 4; j++) if (grid[i + j][column] == 0 || grid[i + j][column] != grid[i + j + 1][column]) check = 0; if (check == 1) return i; } return −1; } void checkColumns(int* n, int grid[gridSize][gridSize]) { for (int i = 0; i < gridSize; i++) { int index = checkCurrentColumn(i, grid); int ball = grid[index][i]; if (index != −1) while (index < gridSize && ball == grid[index][i]) { grid[index++][i] = 0; *n = −−*n; } } } teraz pozostaje napisać dla diagonali − jednak na razie nie mam pomysłu
14 lut 18:45
Filip: Chyba rozdzielić będzie trzeba pętle, przykładowo w jednej zrobić sprawdzenie dla gridij, (1) sprawdzamy dla 4 <= i <= 0 oraz dla 4 <= j <= 9 (2) sprawdzamy dla 4 <= i < 9 oraz dla 1 <= j <= 4
14 lut 18:54
Filip: Chodzi o to, by jedna pętla sprawdzała przykładowo diagonale i inne diagonale pod nią o długości >= 5, a druga diagonale nad główną o długości >= 5
14 lut 18:55
Mariusz: Jak tam postępy ? Dało coś to rozdzielenie pętli ?
16 lut 14:40
Filip: Narazie napisałem tylko dla głównej diagonali oraz diagonali pod nią samą: int checkCurrentDiagonal(int diagonal, int range, int grid[gridSize][gridSize]) { for (int i = 0; i < range; i++) { int check = 1; for (int j = 0; j < 4; j++) if (grid[diagonal + j][i + j] == 0 || grid[diagonal + j][i + j] != grid[diagonal + j + 1][i + j + 1]) check = 0; if (check == 1) return i; } return −1; } void checkDiagonals(int* n, int grid[gridSize][gridSize]) { for (int i = 4; i >= 0; i−−) { int range = 5; int index = checkCurrentDiagonal(i, range − i, grid); int ball = grid[i][index]; printf("%d %d\n", i, index); int k = 0; if (index != −1) while (i + k < gridSize && ball == grid[i + k][index]) { grid[i + k][index++] = 0; *n = −−*n; k++; } range++; } }
16 lut 15:19
Filip: Tak właściwie, to aby sprawdzić diagonale nad główną przekątną, wystarczy zamienić oznaczenia wierszy na kolumny i odwrotnie, przykładowo zamiast grid[i + k][index], dać grid[index][i + k] k++ na k−− tak mi się przynajmniej zdaje, nie patrzyłem jednak czy to będzie działać
16 lut 15:22
Filip: Ajj, zapomniałem o diagonalach w druga stronę może to w takim razie inaczej napiszę, zrobiłem tylko od lewej do prawej
16 lut 15:25
Filip: No to ja tu narazie widzę 4 funkcje do sprawdzania: (1) 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 (2) 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 (3) 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 0 (4) 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Z czego przypadki (1) i (3) oraz (2) i (4) można by spróbować od siebie "uzależnić" i zrobić w jednej funkcji
16 lut 15:36
Mariusz: I co udało ci się napisać te kulki ? Co do tego DFS to udało ci się go napisać ? Jeśli tak to byłby to niezły kontrprzykład na to co amerykańcy wypisują (widziałem ich wpisy że DFS znajduje ścieżkę jeśli istnieje ale nie jest ona najkrótsza a przynajmniej nie zawsze jest najkrótsza)
21 lut 20:57
Filip: DFS znajduje dowolną ścieżkę (o ile istnieje) niekoniecznie najkrótszą. Przykładowo w grafach ważonych używa się algorytm Dijkstry. Jednak pytanie czy tutaj nam to się przyda, ponieważ zajmuje to trochę czasu − tutaj szybsze będzie znalezienie dowolnej drogi
21 lut 21:34
Mariusz: "Pisałem kiedyś dfs'a w grafie ważonym dla listy sąsiedztwa aby szukał najkrótszej drogi z wierzchołka A do B, może to coś pomoże" Wpis z 12 lut 2021 23:48 Jakoś zmodyfikowałeś wtedy oryginalnego DFSa bo amerykańcy pisali w zasadzie to samo co ty w swoim wpisie z 21 lut 2021 21:34 Tutaj preferują użycie BFS i proponują użycie kolejki jako pomocniczej struktury danych tyle że pojedyncze operacje na tablicowej kolejce będą działały w czasie O(n) chyba że zastosujemy operacje modulo do indeksowania tablicy
21 lut 22:05
Filip: Pisałem coś takiego, nie jest tutaj nic specjalnego, po prostu zmodyfikowany dfs i szukanie najkrótszej drogi z możliwych (zrobione klasycznie, wszystkie możliwe drogi, wybranie najmniejszej)− jednak nie wiem czy będzie to optymalne https://pastebin.com/sZsEXjbm W C jest kolejka? Czy samemu trzeba zaimplementować?
21 lut 22:15
Mariusz: W C kolejkę trzeba samemu napisać , w C++ już jest gotowiec, w C# też jest gotowiec napisany na tzw cyklicznej tablicy (tablicę indeksujesz korzystając z operatora modulo )
21 lut 22:36
Mariusz: 1. https://pastebin.com/TuZ1YRfp Powyższy program działa ale zastanów się na jakie funkcje mógłbyś podzielić funkcję sortującą a poza tym jak wyeliminować potrzebę rozdzielania pliku po każdym łączeniu 2. https://pastebin.com/N4FSrydb Tutaj masz program do wyznaczania otoczki wypukłej (Metodę znalazłem gdzieś w sieci i jest ona modyfikacją algorytmu Grahama Do implementacji funkcji sortującej użyłem twojej funkcji łączącej dwie listy) Tutaj nie udało mi się wyeliminować liczników które to ograniczają liczbę punktów z których budujemy otoczkę Masz jakiś pomysł na usunięcie liczników 3. Zastanawiałeś się nad napisaniem klasy liczb wymiernych (Napisana w C++ będzie wygodniejsza w użyciu niż w Javie bo w C++ mamy przeciążanie operatorów)
13 kwi 22:48
Mariusz: Masz pomysł na te trzy zadania ? W drugim zadaniu usunięcie tych liczników nie będzie aż takie trudne W pierwszym usunięcie fazy rozdzielania będzie wymagało trzeciego pliku pomocniczego Jeśli chodzi o trzecie zadanie to po napisaniu tej klasy można by np zilustrować kolejne etapy eliminacji Gaußa od której zaczynaliśmy ten wątek Tutaj liczby wymierne można by wyświetlać i wczytywać za pośrednictwem łańcuchów (przydałoby się napisać wtedy funkcje parseRational oraz toString) Można też użyć przeciążonych operatorów (w C++ bo w Javie nie ma takiej możliwości) W przypadku gdy może wystąpić dzielenie przez zero można by rzucić wyjątek W C++ mamy możliwość zrealizowania operacji arytmetycznych przeciążając operatory przez co klasa będzie wygodniejsza w użyciu Co do prywatnych metod to mogłaby się tu znaleźć np funkcja licząca nwd algorytmem Euklidesa
14 kwi 16:24
Philips: Jak widzisz wczoraj umiesciłem tutaj dwa wpisy z głównego konta, jednak dzisiaj ich już nie ma. Wygląda na to, iż nadal posiadam tam blokadę, jak wszystko się ustabilizuję to powróce
16 kwi 10:29
Emily Thomas: Great discussion on Gauss elimination and coding! Both can be quite challenging but incredibly rewarding once you get the hang of them. It’s fascinating how mastering such techniques can open up new avenues in problem−solving. Just like diving into complex mathematical algorithms or coding, finding the right support can make a huge difference in tackling academic tasks. For anyone struggling with assignments or looking for a little extra help, MyAssignmentHelp offers top−notch assignment helpers who can guide you through even the trickiest of problems. Their expertise ensures you get clear, well−structured assistance tailored to your needs. So if you find yourself stuck, don’t hesitate to explore how MyAssignmentHelp can make your academic journey smoother and more manageable! Here you can find assignment helpers services: https://myassignmenthelp.co.uk/
27 sie 08:57
trastynmu: To leap and fly, you just touch the screen, which requires exact time and coordination. https://geometrydashsubzero.net
30 paź 05:06