Gramatyka bezkontekstowa
Filip:
Witam, czy jest tutaj ktos kto ma pojecie na temat gramatyki bezkontekstowej?
1 gru 13:58
Filip:
Mam taka gramatyke bezkontekstowa
S→aSa|bSb|a|b|ε
I przykladowo dla n = 6 dostaje
{a, b, aaa, aba, bab, bbb}
?
1 gru 15:01
Pytający:
S→aSa|bSb|a|b|ε
nie jest gramatyką (aczkolwiek można się domyślić, o jakiej gramatyce mówisz).
Jednak czym jest to "n"?
1 gru 15:53
Filip:
n to liczba lancuchow. Pelna tresc polecenia brzmi:
Napisz program, ktory wypisze na ekranie n lancuchow z jezyka opisanego
za pomoca gramatyki bezkontekstowej:
S→aSa|bSb|a|b|ε
Program powinien wyswietlac opis gramatyki bezkontekstowej, a wypisywane lancuchy powinny byc
uporzadkowane w postaci kanonicznej
Odchodzac od napisania prgoramu, narazie mam sam problem ze zrozumieniem jak tworzyc te
lancuchy
1 gru 16:01
Filip:
Okej, jak rozumiem...
Zaczynam od S i moge je zamienic na aSa lub bSb lub a lub b lub lancuch pusty ktory przerywa
generowanie.
na przyklad zamieniam na aSa i tutaj znowu powtarzam czynnosc. Wybieram dowolny lancuch, jednak
co sie stanie jesli za S wstaiw a lub b?
Otrzymam wtedy aab lub aba i juz nie mam tutaj S, wiec koncze generowanie?
1 gru 16:47
Filip:
No chyba ze ide od "lewej do prawej" czyli
Jak zamienie S na bSb to pozniej bSb bede mogl tylko zamienic na a lub b lub lanuch pusty?
1 gru 16:49
Pytający:
Nie bardzo wiem, co znaczy tutaj "uporzadkowane w postaci kanonicznej".
Generalnie każde S możesz zamienić na aSa albo bSb albo a albo b albo ε.
Znaczy jeśli zaczynasz od S to w "pierwszej iteracji" możesz otrzymać:
• aSa
• bSb
• a // nie ma co tu więcej zamieniać, znaczy jest to słowo generowane przez tę gramatykę
• b // słowo
• ε // słowo
W "drugiej iteracji" zaczynasz z aSa i bSb i możesz otrzymać:
// aSa:
• aaSaa
• abSba
• aaa // słowo
• aba // słowo
• aa // słowo
// bSb:
• baSab
• bbSbb
• bab // słowo
• bbb // słowo
• bb // słowo
I tak dalej.
Generalnie ta gramatyka generuje wszystkie palindromy nad alfabetem {a, b}.
Nie wiem, które 6 masz tu wypisać, może {ε, a, b, aa, bb, aaa} (ale to strzał).
1 gru 17:14
Husten Saft: Klusek jest taki aforyzm G Eliota
Blogoslawiony niech bedzie czlowiek ktory nie majac nic do powiedzenia nie dostarcza tego
dowodu w slowach
1 gru 17:52
Filip:
W postaci kanonicznej to znaczy uporzadkowac rosnaco/malejaco wzgledem dlugosci lancucha, a
lancuchy o tej samej dlugosci alfabetycznie.
Zapewne kolejne utworzeone lancuchy bede wrzucac do wektora, i przydaloby sie napisac funkcje
sortujaca, ktora sortuje zarowno rosnaco/malejaca, a nastepnie alfabetycznie te lancuchy,
ktore trzeba
1 gru 19:24
Filip:
Taki pomysl od razu przyszedl mi na mysl, jeszcze nie podchdozilem do tego problemu w kodzie.
Naracie chcialem zrozumiec sama koncepcje
1 gru 19:25
Filip:
@Pytajacy...
chyba rozgryzlem patent na jakies zasadzie to dziala
1) dla aSa mam {aaa, aa, aba}
dla bSb mam {bbb, bb, bab}
2) dla aaSaa mam {aaaaa, aaaa, aabaa} − czyli dodaje po jedno a z przodu i z tylu od lanuchow
aSa
dla abSba mam {abba, abbba, ababa} − czyli dodaje po jedno a z prozdu i z tylu od lancuchow
bSb
2.1) dla baSab mam {baaab, baab, babab} − czyli dodaje po jedno b z przodu i z tylu lancuchow
aSa
dla bbSbb mam {bbbbb, bbabb, bbbb} − czyli dodaje po jedno b z przodu i z tylu lancuchow
bSb
No i jak przejde taki jeden cykl, zwiekszam liczbe dodawanych liter z przodu i z tylu o 1
Napisalem w kodzie to tak, prototyp, nie wyglada zbyt ladnie:
char aSa[3][4] = { {'a', 'a', 'a', '\0'}, {'a', 'b', 'a', '\0'}, {'a', 'a', '\0', 'c'} };
char bSb[3][4] = { {'b', 'b', 'b', '\0'}, {'b', 'a', 'b', '\0'}, {'b', 'b', '\0', 'c'} };
int a = 5;
int k = 1;
while (a > 0) {
for (int i = 0; i < 3; i++) {
for (int i = 0; i < k; i++) {
printf("a");
}
printf("%s", aSa[i]);
for (int i = 0; i < k; i++) {
printf("a");
}
printf("\n");
}
for (int i = 0; i < 3; i++) {
for (int i = 0; i < k; i++) {
printf("a");
}
printf("%s", bSb[i]);
for (int i = 0; i < k; i++) {
printf("a");
}
printf("\n");
}
for (int i = 0; i < 3; i++) {
for (int i = 0; i < k; i++) {
printf("b");
}
printf("%s", aSa[i]);
for (int i = 0; i < k; i++) {
printf("b");
}
printf("\n");
}
for (int i = 0; i < 3; i++) {
for (int i = 0; i < k; i++) {
printf("b");
}
printf("%s", bSb[i]);
for (int i = 0; i < k; i++) {
printf("b");
}
printf("\n");
}
printf("\n");
k++;
a−−;
}
1 gru 21:11
Filip: Output tego programu jest taki... wyglada na to ze potworzyly sie palindromy
aaaaa
aabaa
aaaa
abbba
ababa
abba
baaab
babab
baab
bbbbb
bbabb
bbbb
aaaaaaa
aaabaaa
aaaaaa
aabbbaa
aababaa
aabbaa
bbaaabb
bbababb
bbaabb
bbbbbbb
bbbabbb
bbbbbb
aaaaaaaaa
aaaabaaaa
aaaaaaaa
aaabbbaaa
aaababaaa
aaabbaaa
bbbaaabbb
bbbababbb
bbbaabbb
bbbbbbbbb
bbbbabbbb
bbbbbbbb
1 gru 21:18
Filip:
Udalo mi sie napisac dzialajacy kod, wyswietlajacy n palindromow...
#define CRTSECURENOWARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp(const void* a, const void* b) {
char* f = (char*)a;
char* s = (char*)b;
return strlen(f) − strlen(s);
}
int main() {
int n;
scanf("%d", &n);
//char aSa[3][4] = { {'a', 'a', 'a', '\0'}, {'a', 'b', 'a', '\0'}, {'a', 'a', '\0', 'c'} };
//char bSb[3][4] = { {'b', 'b', 'b', '\0'}, {'b', 'a', 'b', '\0'}, {'b', 'b', '\0', 'c'} };
char** aSa;
aSa = malloc(3 * sizeof(char*));
aSa[0] = malloc(4 * sizeof(char));
aSa[0] = "aaa";
aSa[1] = malloc(4 * sizeof(char));
aSa[1] = "aba";
aSa[2] = malloc(3 * sizeof(char));
aSa[2] = "aa";
char** bSb;
bSb = malloc(3 * sizeof(char*));
bSb[0] = malloc(4 * sizeof(char));
bSb[0] = "bbb";
bSb[1] = malloc(4 * sizeof(char));
bSb[1] = "bab";
bSb[2] = malloc(3 * sizeof(char));
bSb[2] = "bb";
char t[1000][1000];
int m = 0;
int a = 10;
int k = 1;
while (a > 0) {
for (int i = 0; i < 3; i++) {
char* c = malloc(sizeof(char) * 1000 + 2 * k);
for (int j = 0; j < k; j++) {
c[j] = 'a';
}
strcpy(c + k, aSa[i]);
char* p = malloc(k * sizeof(char) + 1);
for (int i = 0; i < k; i++) {
p[i] = 'a';
}
p[k] = '\0';
strcat(c + k + strlen(aSa[i]), p);
strcpy(t[m++], c);
//printf("%d\n", strlen(c));
/*
for (int i = 0; i < k; i++) {
printf("a");
}
printf("%s", aSa[i]);
for (int i = 0; i < k; i++) {
printf("a");
}
//printf()
*/
}
for (int i = 0; i < 3; i++) {
char* c = malloc(sizeof(char) * 1000 + 2 * k);
for (int j = 0; j < k; j++) {
c[j] = 'a';
}
strcpy(c + k, bSb[i]);
char* p = malloc(k * sizeof(char) + 1);
for (int i = 0; i < k; i++) {
p[i] = 'a';
}
p[k] = '\0';
strcat(c + k + strlen(bSb[i]), p);
strcpy(t[m++], c);
//printf("%d\n", strlen(c));
}
for (int i = 0; i < 3; i++) {
char* c = malloc(sizeof(char) * 1000 + 2 * k);
for (int j = 0; j < k; j++) {
c[j] = 'b';
}
strcpy(c + k, aSa[i]);
char* p = malloc(k * sizeof(char) + 1);
for (int i = 0; i < k; i++) {
p[i] = 'b';
}
p[k] = '\0';
strcat(c + k + strlen(aSa[i]), p);
strcpy(t[m++], c);
//printf("%d\n", strlen(c));
}
for (int i = 0; i < 3; i++) {
char* c = malloc(sizeof(char) * 1000 + 2 * k);
for (int j = 0; j < k; j++) {
c[j] = 'b';
}
strcpy(c + k, bSb[i]);
char* p = malloc(k * sizeof(char) + 1);
for (int i = 0; i < k; i++) {
p[i] = 'b';
}
p[k] = '\0';
strcat(c + k + strlen(bSb[i]), p);
strcpy(t[m++], c);
//printf("%d\n", strlen(c));
}
k++;
a−−;
}
strcpy(t[m++], "");
strcpy(t[m++], "a");
strcpy(t[m++], "b");
for (int i = 0; i < 3; i++) {
strcpy(t[m++], aSa[i]);
strcpy(t[m++], bSb[i]);
}
qsort(t, m, 1000, cmp);
printf("[");
for (int i = 0; i < n; i++) {
if (i != n − 1) {
printf("%s, ", t[i]);
} else {
printf("%s]", t[i]);
}
}
}
1 gru 23:31
Pytający:
Zauważ, że:
• masz jedynie dwie litery w alfabecie,
• palindrom jest jednoznacznie zdefiniowany poprzez prefix + ewentualną środkową literę (gdy
długość palindromu jest nieparzysta).
Stąd przyszło mi na myśl, żeby palindromy generować z binarnej postaci kolejnych liczb
całkowitych danej długości. Przykładowo:
• palindromy długości 0:
// (binarnie prefix + ewentualna środkowa) → (binarnie całość) → (literalnie całość)
ε → ε → ε
• palindromy długości 1:
// nieparzysta długość, więc nie powielasz ostatniej cyfry / litery
0 → 0 → a
1 → 1 → b
• palindromy długości 2:
0 → 00 → aa
1 → 11 → bb
• palindromy długości 3:
// nieparzysta długość, więc nie powielasz ostatniej cyfry / litery
00 → 000 → aaa
01 → 010 → aba
10 → 101 → bab
11 → 111 → bbb
• palindromy długości 4:
00 → 0000 → aaaa
01 → 0110 → abba
10 → 1001 → baab
11 → 1111 → bbbb
itd.
Takich palindromów długości d = 2k jest 2
k.
Takich palindromów długości d = 2k + 1 jest 2
k+1.
Stąd takowa implementacja:
https://ideone.com/q4zNxI
2 gru 17:17
2 gru 17:25
Filip:
Pytajacy, dziekuje ci za kod, kod ktory napisalem powyzej jest bledny, poniewaz dla lancucha o
dlugosci i tworzy tylko 4 palindromy. Po przeanalizowaniu tego co napisales, zgadzam sie z
toba, ze dla parzystych dostaje
kolejno o dlugosci kolejnych lancuchow grupujac na dlugosci parzyste/nieparzyste bedzie rosnac
wykladniczo, natomiast mam prosbe, moglbys zerknac w moj kod i poradzic mi, dlaczego
maksymalna liczba palindromow, ktora moge wypisac wynosi 509?
Bylbym wdzieczny
Podrzucam kod, wedlug mnie jest to spowodowane zbyt duzym zajeciem pamieci
przez program
https://pastebin.com/idW0XFmX
6 gru 18:13
Pytający:
Nigdzie podczas generowania palindromów nie uwzględniasz wejściowego "n", pętlę (linia 53.)
wykonujesz zawsze 6 razy.
2*∑j=05(2j+2) + 5 = 509
6 gru 19:28
Filip:
No wlasnie jak juz chce 7 iteracji petli program sie crashuje
6 gru 19:29
Filip: Tak wlasciwie jak uzaleznic te petle zewnetrzna od wejsciowego n?
6 gru 19:31
Pytający:
Nic dziwnego, że się crashuje skoro robisz rzeczy typu:
char temp[500][32];
czy
char* c = malloc(sizeof(char) * 1000 + 2 * k);
jakieś uzasadnienie skąd te liczby czy "tak o, na oko"?
A jak uzależnić od n?
Analogicznie jak u mnie: na starcie przecież wiesz, ile jest palindromów danej długości i
wiesz, jaką długość obecnie generujesz.
A jeśli chcesz wszystkie wygenerowane palindromy przechowywać w pamięci (a nie np. jedynie
wypisywać na bieżąco), to też można wyliczyć na podstawie którego wcześniej wygenerowanego
palindromu należy generować kolejny. Nie trzeba jakoś oddzielnie pamiętać parzystych /
nieparzystych.
6 gru 20:26
Filip: Tak, wlasnie probuje teraz zminimalizowac zajecie pamieci
6 gru 21:08
Filip: A te liczby to tak na oko wzialem, to sie zgadza
6 gru 21:09