Podstawy Programowania – Wykład 12
OPERATORY BITOWE
Uwaga: Operatorów tych nie moŜ na uŜ ywać do typu float lub double.
&
bitowa koniunkcja
|
bitowa alternatywa
^
bitowa róŜnica symetryczna ~
uzupełnienie jedynkowe
>> przesunięcie w prawo
<< przesunięcie w lewo
Koniunkcja bitowa &
Stosowana często do "zasłaniania" pewnego zbioru bitów, np.:
num & 0177;
spowoduje wyzerowanie wszystkich oprócz siedmiu najmłodszych bitów wartości zmiennej num.
Operatory przesunięcia >> oraz <<
SłuŜą do przesuwania argumentu stojącego po lewej stronie operatora o liczbę pozycji określoną przez argument stojący po prawej stronie. Np.:
num << 2;
przesuwa num w lewo o dwie pozycje, zwolnione bity wypełniane są zerami (operacja równowaŜna mnoŜeniu przez 4).
num >> 2;
dla wielkości typu unsigned zwolnione bity zawsze są wypełniane zerami. Dla wielkości ze znakiem spowoduje to wypełnienie tych miejsc bitem znaku (przesunięcie arytmetyczne), a dla wielkości bez znaku –
wypełniane zerami (przesunięcie logiczne).
W12 - 1
Podstawy Programowania – Wykład 12
#include <stdio.h> /*konwersja z kodu hex na binarny; przyklad W12P00*/
int main()
{ int j, num=0xff, bit;
unsigned int mask;
while(num!=0)
{ mask=0x80000000;
printf("Wprowadz liczbe: "); scanf("%x", &num);
printf("Rozwinieciem binarnym %08x jest: \n", num);
for(j=0; j<32; j++)
{ bit=(mask&num)?1:0; printf("%d", bit);
if(!((j+1)%4)&&(j<28)) printf("--");
mask>>=1;
}
printf("\n\n");
}
}
Wprowadz liczbe: abde457f
Rozwinieciem binarnym abde457f jest:
1010--1011--1101--1110--0100--0101--0111--1111
Wprowadz liczbe: 012345678
Rozwinieciem binarnym 12345678 jest:
0001--0010--0011--0100--0101--0110--0111--1000
Wprowadz liczbe: 9abcdef0
Rozwinieciem binarnym 9abcdef0 jest:
1001--1010--1011--1100--1101--1110--1111--0000
Wprowadz liczbe: 0
W12 - 2
Podstawy Programowania – Wykład 12
Bitowy operator alternatywy |
Stosowany do "ustawiania" bitów, np.:
num | mask;
ustawia 1 na tych bitach w zmiennej num, które w mask są równe 1.
Bitowa róŜnica symetryczna ^
Ustawia jedynkę na kaŜdej pozycji bitowej tam, gdzie bity w obu argumentach są róŜne, a zero tam, gdzie są
takie same.
Dopełnienie jedynkowe ~
Zamienia kaŜdy bit 1 na 0 i odwrotnie. Np.:
num & ~037;
„zasłania” zerami ostatnie 5 bitów zmiennej num (o dowolnym rozmiarze!!!). Zapis:
num & 0177740;
powoduje ten sam efekt, przy załoŜeniu, Ŝe słowo jest 16-bitowe.
/*operatory bitowe; przyklad W12P01*/
#define PRINT(f,x) printf(""#x" = %"#f"\n",(x))
#define PRINT2(f,x,y) printf(""#x"=%"#f", "#y"=%"#f"\n",(x),(y))
#include <stdio.h>
#include <limits.h>
void prbin(int);
unsigned getbits(unsigned x, int p, int n);
/*"wyciaga" n bitow ze zmiennej x od pozycji p*/
int razy10(int n);
/*mnozenie liczby calkowitej przez 10 za pomoca operatorow przesuniecia*/
W12 - 3
Podstawy Programowania – Wykład 12
int main()
{
int num=19876543210;
int i=1;
int j=23;
int mask=017777;
unsigned val;
PRINT(d,num); prbin(num);
PRINT(d,mask); prbin(mask);
PRINT(d,num & mask); /*zerowanie bitow*/
prbin(num & mask);
PRINT(d,num | mask); /*ustawianie bitow*/
prbin(num | mask);
PRINT(d,num ^ mask); /*roznica symetryczna bitow*/
prbin(num ^ mask);
PRINT(d, num << 8); /*mnozenie*/
prbin(num << 8);
PRINT(d, num >> 9); /*powielenie znaku (p.arytmet.)*/
prbin(num >> 9);
PRINT(d, ~num); /*dopelnienie jedynkowe*/
prbin(~num);
PRINT2(d,i,j);
PRINT2(d,i&j,i&&j);
printf("getbits(num,14,12) = %d\n", val=getbits(num,14,12));
prbin(val);
PRINT(d,j);
prbin(j);
PRINT(d,razy10(j));
prbin(razy10(j));
}
W12 - 4
Podstawy Programowania – Wykład 12
unsigned getbits(unsigned x, int p, int n)
{
prbin(~0<<n);
prbin(x>>(p+1-n));
return (x>>(p+1-n)) & ~(~0<<n);
}
void prbin(int znak)
{
int j;
unsigned mask=INT_MIN;
for(j=0;j<sizeof(int)*8;j++)
{
printf("%d", (mask&znak)?1:0);
if(!((j+1)%4)&&(j<sizeof(int)*7)) printf("--");
mask>>=1;
}
printf("\n");
}
int razy10(int n)
{
int m, p;
m=n<<1;
p=m<<2;
return m+p;
}
num = -1598293270
1010--0000--1011--1011--1111--1010--1110--1010
mask = 8191
0000--0000--0000--0000--0001--1111--1111--1111
W12 - 5
Podstawy Programowania – Wykład 12
num & mask = 6890
0000--0000--0000--0000--0001--1010--1110--1010
num | mask = -1598291969
1010--0000--1011--1011--1111--1111--1111--1111
num ^ mask = -1598298859
1010--0000--1011--1011--1110--0101--0001--0101
num << 8 = -1141184000
1011--1011--1111--1010--1110--1010--0000--0000
num >> 9 = -3121667
1111--1111--1101--0000--0101--1101--1111--1101
~num = -3121667
0101--1111--0100--0100--0000--0101--0001--0101
i=1, j=23
i&j=1, i&&j=1
1111--1111--1111--1111--1111--0000--0000--0000
0001--0100--0001--0111--0111--1111--0101--1101
getbits(num,14,12) = 3933
0000--0000--0000--0000--0000--1111--0101--1101
j=23
0000--0000--0000--0000--0000--0000--0001--0111
razy10(j) = 230
0000--0000--0000--0000--0000--0000--1110--0110
Operacje bitowe na znakach
Kody ASCII
Bit7
Bit6
Bit5
Bit4
Bit3
Bit2
Bit1
Bit0
0 – cyfry
0 – duŜe litery
0 – a-o
1 – litery
1 – cyfry / małe litery
1 – cyfry / p-z
W12 - 6
Podstawy Programowania – Wykład 12
#include <stdio.h> /*operacje na znakach; przyklad W12P02*/
void prbin(char znak);
char toupper(char c); /*funkcja zamienia male litery na duze*/
char tolower(char c); /*funkcja zamienia duze litery na male*/
char swapcase(char c); /*zamiana duzych liter na male i odwrotnie*/
int main()
{
char ch1='A', ch2='l', ch3='m';
prbin(ch1);
prbin(tolower(ch1));
prbin(ch2);
prbin(toupper(ch2));
prbin(ch3);
prbin(ch3=swapcase(ch3));
prbin(ch3=swapcase(ch3));
}
void prbin(char znak)
{
int j;
unsigned mask=0x80;
printf("znak=%c, binarnie:", znak);
for(j=0;j<sizeof(char)*8;j++)
{
printf("%d", (mask&znak)?1:0);
if(!((j+1)%4)&&(j<sizeof(char)*7))
printf("--");
mask>>=1;
}
printf("\n");
}
W12 - 7
Podstawy Programowania – Wykład 12
char toupper(char c)
{
char maska=223; /*223 – 11011111*/
return c & maska;
}
char tolower(char c)
{
return c | 32; /*32 – 00100000*/
}
char swapcase(char c)
{
return c ^ 32;
}
znak=A, binarnie:0100--0001
znak=a, binarnie:0110--0001
znak=l, binarnie:0110--1100
znak=L, binarnie:0100--1100
znak=m, binarnie:0110--1101
znak=M, binarnie:0100--1101
znak=m, binarnie:0110--1101
/*kalkulator bitowy; przyklad W12P03*/
#include <stdio.h>
#include <limits.h>
void printbin(int num);
void pline(void);
int main()
W12 - 8
Podstawy Programowania – Wykład 12
{
char op[10];
unsigned int x=0xff, y=0xff;
while(x!=0||y!=0)
{
printf("Wprowadz wyrazenie (np. abc & 789): ");
scanf("%x %s %x", &x, op, &y);
printf("\n");
switch(op[0])
{
case '&':printbin(x); printf("\t\t& (and)\n");
printbin(y); pline(); printbin(x&y);
printf("%x %c %x = %x\n\n", x, '&', y, x&y);
break;
case '|':printbin(x); printf("\t\t| (or)\n");
printbin(y); pline(); printbin(x|y);
printf("%x %c %x = %x\n\n", x, '|', y, x|y);
break;
case '^':printbin(x); printf("\t\t^ (xor)\n");
printbin(y); pline(); printbin(x^y);
printf("%x %c %x = %x\n\n", x, '^', y, x^y);
break;
case '>':printbin(x); printf("\t\t>> %d\n", y);
pline(); printbin(x>>y);
printf("%x %s %x = %x\n\n", x, ">>", y, x>>y); break;
case '<':printbin(x); printf("\t\t<< %d\n", y);
pline(); printbin(x<<y);
printf("%x %s %x = %x\n\n", x, "<<", y, x<<y); break;
W12 - 9
Podstawy Programowania – Wykład 12
case '~':printbin(x);
printf("\t\t~ (uzupelnienie)\n");
pline(); printbin(~x);
printf("%c%x = %x\n\n", '~', x, ~x);
break;
default: printf("Niepoprawny operator!\n"); break;
}
} /*koniec while*/
} /*koniec main*/
void pline(void)
{
int j;
for(j=1; j<sizeof(int)*12; j++)
printf("-");
printf("\n");
}
void printbin(int num)
{ int j, bit;
unsigned int mask;
mask=INT_MIN;
for(j=0;j<sizeof(int)*8;j++)
{
bit=(mask&num)?1:0;
printf("%d",bit);
if(!((j+1)%4)&&(j<sizeof(int)*7))
printf("--");
mask>>=1;
}
printf("\n");
}
W12 - 10
Podstawy Programowania – Wykład 12
Wprowadz wyraŜenie (np. abc & 789): abcdef97 & 45678932
1010--1011--1100--1101--1110--1111--1001--0111
& (and)
0100--0101--0110--0111--1000--1001--0011--0010
-----------------------------------------------
0000--0001--0100--0101--1000--1001--0001--0010
abcdef97 & 45678932 = 1458912
Wprowadz wyraŜenie (np. abc & 789): abcdef97 | 45678932
1010--1011--1100--1101--1110--1111--1001--0111
| (or)
0100--0101--0110--0111--1000--1001--0011--0010
-----------------------------------------------
1110--1111--1110--1111--1110--1111--1011--0111
abcdef97 | 45678932 = efefefb7
Wprowadz wyraŜenie (np. abc & 789): abcdef97 ^ 45678932
1010--1011--1100--1101--1110--1111--1001--0111
^ (xor)
0100--0101--0110--0111--1000--1001--0011--0010
-----------------------------------------------
1110--1110--1010--1010--0110--0110--1010--0101
abcdef97 ^ 45678932 = eeaa66a5
Wprowadz wyraŜenie (np. abc & 789): abcdef97 ~ 0
1010--1011--1100--1101--1110--1111--1001--0111
W12 - 11
Podstawy Programowania – Wykład 12
~ (uzupelnienie)
-----------------------------------------------
0101--0100--0011--0010--0001--0000--0110--1000
~abcdef97 = 54321068
Wprowadz wyraŜenie (np. abc & 789): abcdef97 >> 5
1010--1011--1100--1101--1110--1111--1001--0111
>> 5
-----------------------------------------------
0000--0101--0101--1110--0110--1111--0111--1100
abcdef97 >> 5 = 55e6f7c
Wprowadz wyraŜenie (np. abc & 789): abcdef97 << 5
1010--1011--1100--1101--1110--1111--1001--0111
<< 5
-----------------------------------------------
0111--1001--1011--1101--1111--0010--1110--0000
abcdef97 << 5 = 79bdf2e0
Wprowadz wyraŜenie (np. abc & 789): 0 o 0
Niepoprawny operator!
Wprowadz wyraŜenie (np. abc & 789): 0 o 0
/*szyfrowanie tekstow; przyklad W12P04*/
#include <stdio.h>
void szyfruj(char *);
void prbin(int);
W12 - 12
Podstawy Programowania – Wykład 12
int main()
{
char zdanie[50], dlug;
do
{
puts("wprowadz tekst do zaszyfrowania:");
gets(zdanie);
if((dlug=strlen(zdanie))!=0)
{
szyfruj(zdanie);
puts("A teraz odszyfrowanie");
szyfruj(zdanie);
}
}
while(dlug);
}
void szyfruj(char *zdanie)
{
char klucz[4]="4gw\n";
int i=0, z;
while((z=zdanie[i])!='\0')
{
printf("Wej=>%c=%03d=%02x->", (z>32&&z<127)?z:' ', z, z); prbin(z);
z=zdanie[i++]=z^klucz[i%4];
printf(" Wyj=%c=%03d=%02x=>", (z>32&&z<127)?z:' ', z, z); prbin(z);
printf("\n");
}
}
W12 - 13
Podstawy Programowania – Wykład 12
void prbin(znak)
{
int j,
int mask=0x80;
for(j=0;j<8;j++)
{
printf("%d", (mask&znak)?1:0);
if(j==3)
printf("-");
mask>>=1;
}
}
wprowadz tekst do zaszyfrowania:
Grodno
Wej=>G=071=47->0100-0111 Wyj=s=115=73=>0111-0011
Wej=>r=114=72->0111-0010 Wyj= =021=15=>0001-0101
Wej=>o=111=6f->0110-1111 Wyj= =024=18=>0001-1000
Wej=>d=100=64->0110-0100 Wyj=n=110=6e=>0110-1110
Wej=>n=110=6e->0110-1110 Wyj=Z=090=5a=>0101-1010
Wej=>o=111=6f->0110-1111 Wyj= =008=08=>0000-1000
A teraz odszyfrowanie
Wej=>s=115=73->0111-0011 Wyj=G=071=47=>0100-0111
Wej=> =021=15->0001-0101 Wyj=r=114=72=>0111-0010
Wej=> =024=18->0001-1000 Wyj=o=111=6f=>0110-1111
Wej=>n=110=6e->0110-1110 Wyj=d=100=64=>0110-0100
Wej=>Z=090=5a->0101-1010 Wyj=n=110=6e=>0110-1110
Wej=> =008=08->0000-1000 Wyj=o=111=6f=>0110-1111
wprowadz tekst do zaszyfrowania:
W12 - 14
Podstawy Programowania – Wykład 12
Rekurencyjny-matematyka dają cy się wyrazić za pomocą wielkoś ci uprzednio znanych; wzór rekurencyjny-
wzór pozwalają cy obliczyć wyrazy cią gu na podstawie jednego lub kilku wyrazów poprzedzają cych.
<ang. recurrent, fr. recurrent, z łac. recurrens'powracają cy'>
[Słownik Wyrazów Obcych, PWN, 1996]
Algorytm rekurencyjny w trakcie wykonywania odwołuje się do samego siebie. Aby mógł się zakończyć, jego kolejne odwołania do siebie samego muszą zaleŜeć od pewnego warunku, który zmienia się z kaŜdym kolejnym odwołaniem. Dzięki takim algorytmom, rozwiązania trudnych problemów mogą być bardzo proste.
Rekurencja oznacza wywołanie funkcji przez tę samą funkcję. Aby wyznaczyć n-tą wartość trzeba najpierw wyznaczyć wszystkie „wcześniejsze” wartości. WaŜne jest, aby kolejne wywołania funkcji rekurencyjnej były realizowane dla kolejnych wartości parametrów formalnych w taki sposób, aby nie doszło do zjawiska
„nieskończonej pętli rekurencyjnych wywołań funkcji”.
Kolejne wywołania funkcji rekurencyjnej są związane z odkładaniem na stosie programu kolejnych rekordów aktywacji funkcji. W wyniku kończenia działania poszczególnych funkcji na kolejnych poziomach rekurencji kolejne rekordy aktywacji są zdejmowane ze stosu.
Silnia - algorytm rekurencyjny:
n!=1
dla n=0 (warunek zakończenia rekurencji)
n!=n*(n-1)! dla n>0
Silnia - algorytm sekwencyjny (iteracyjny):
n!= (n-1)!*n dla n>0
/*rekurencja-silnia; przyklad W12P05*/
#include <stdio.h>
#include <stdlib.h>
long silniarek(int n); /*wersja rekurencyjna*/
long silnianrk(int n); /*wersja iteracyjna*/
W12 - 15
Podstawy Programowania – Wykład 12
int main()
{
char ciag[20];
int liczba;
long sil;
puts("REKURENCYJNE I ITERACYJNE OBLICZANIE SILNI");
do
{
printf("Wprowadz liczbe: ");
gets(ciag);
liczba=atoi(ciag);
if(liczba%2)
sil=silnianrk(liczba);
else
sil=silniarek(liczba);
if(sil>=0)
printf("(%d)!=%ld\n", liczba,sil);
}
while(liczba>=0);
}
long silniarek(int n)
{
int i;
printf("Adres i=%u\n", &i);
if(n>=0)
{
if(n==0 || n==1)
return (1);
else
W12 - 16
Podstawy Programowania – Wykład 12
return(n*silniarek(n-1));
}
else
printf("(%d)!=Wartosc nieokreslona\n", n);
return (-1);
}
long silnianrk(int n)
{
long s=1;
int i;
if(n>=0)
{
if(n==0 || n==1)
return (1);
else
{
for(i=2;i<=n;i++)
s*=i;
return(s);
}
}
else
printf("(%d)!=Wartosc nieokreslona\n", n);
return (-1);
}
REKURENCYJNE I ITERACYJNE OBLICZANIE SILNI
Wprowadz liczbe: 0
Adres i=2293508
W12 - 17
Podstawy Programowania – Wykład 12
(0)!=1
Wprowadz liczbe: 1
(1)!=1
Wprowadz liczbe: 2
Adres i=2293508
Adres i=2293476
(2)!=2
Wprowadz liczbe: 3
(3)!=6
Wprowadz liczbe: 4
Adres i=2293508
Adres i=2293476
Adres i=2293444
Adres i=2293412
(4)!=24
Wprowadz liczbe: 5
(5)!=120
Wprowadz liczbe: 6
Adres i=2293508
Adres i=2293476
Adres i=2293444
Adres i=2293412
Adres i=2293380
Adres i=2293348
(6)!=720
Wprowadz liczbe: -1
(-1)!=Wartość nieokreslona
W12 - 18
Podstawy Programowania – Wykład 12
Ciąg Fibonacciego:
F(n)=n
dla n<2
F(n)=F(n-2)+F(n-1) dla n>=2
Rekurencyjna implementacja ciągu Fibonacciego jest niezwykle nieefektywna – ma bardzo duŜą „złoŜoność
pamięciową”. Zdecydowanie bardziej efektywna jest implementacja iteracyjna.
Liczba dodawań
Liczba wywołań
Liczba przypisań
n
w algorytmie
algorytmu
algorytmu
rekurencyjnym
rekurencyjnego
iteracyjnego
6
12
25
15
10
88
177
27
15
986
1 973
42
20
10 945
21 891
57
25
121 392
242 785
72
30
1 346 268
2 692 537
87
/*rekurencja-Fibonacci; przyklad W12P06*/
#include <stdio.h>
#include <stdlib.h>
long fibrek(int n); /*wersja rekurencyjna*/
long fibnrk(int n); /*wersja iteracyjna*/
int main()
{
char ciag[20];
int liczba;
long fib;
puts("WYRAZY CIAGU FIBONACCIEGO (REKUR. I ITERAC.)");
do
{
W12 - 19
Podstawy Programowania – Wykład 12
printf("Wprowadz liczbe: ");
gets(ciag);
liczba=atoi(ciag);
if(liczba>=0)
{
if(liczba%2)
fib=fibrek(liczba);
else
fib=fibnrk(liczba);
printf("%d. wyraz ciagu Fibonacciego=%ld\n", liczba,fib);
}
else
puts("Zla wartosc - koniec obliczen");
}
while(liczba>=0);
}
long fibrek(int n)
{
int i;
printf("Adres i=%u\n",&i);
if(n<3)
return(1);
else
return(fibrek(n-1)+fibrek(n-2));
}
long fibnrk(int n)
{
long f1=1, f2=1, f=1;
int i;
for(i=3;i<=n;i++)
W12 - 20
Podstawy Programowania – Wykład 12
{
f=f1+f2;
f2=f1;
f1=f;
}
return(f);
}
WYRAZY CIAGU FIBONACCIEGO (REKUR. I ITERAC.)
Wprowadz liczbe: 0
0. wyraz ciagu Fibonacciego=1
Wprowadz liczbe: 1
Adres i=2293504
1. wyraz ciagu Fibonacciego=1
Wprowadz liczbe: 2
2. wyraz ciagu Fibonacciego=1
Wprowadz liczbe: 3
Adres i=2293504
Adres i=2293472
Adres i=2293472
3. wyraz ciagu Fibonacciego=2
Wprowadz liczbe: 4
4. wyraz ciagu Fibonacciego=3
Wprowadz liczbe: 5
Adres i=2293504
Adres i=2293472
Adres i=2293440
Adres i=2293408
Adres i=2293408
Adres i=2293440
W12 - 21
Podstawy Programowania – Wykład 12
Adres i=2293472
Adres i=2293440
Adres i=2293440
3. wyraz ciagu Fibonacciego=2
Wprowadz liczbe: 6
6. wyraz ciagu Fibonacciego=8
Wprowadz liczbe: 7
Adres i=2293504
Adres i=2293472
Adres i=2293440
Adres i=2293408
Adres i=2293376
Adres i=2293344
Adres i=2293344
Adres i=2293376
Adres i=2293408
Adres i=2293376
Adres i=2293376
Adres i=2293440
Adres i=2293408
Adres i=2293376
Adres i=2293376
Adres i=2293408
Adres i=2293472
Adres i=2293440
Adres i=2293408
Adres i=2293376
Adres i=2293376
Adres i=2293408
Adres i=2293440
W12 - 22
Podstawy Programowania – Wykład 12
Adres i=2293408
Adres i=2293408
7. wyraz ciagu Fibonacciego=13
Wprowadz liczbe: 8
8. wyraz ciagu Fibonacciego=21
Wprowadz liczbe: 28
28. wyraz ciagu Fibonacciego=317811
Wprowadz liczbe: 38
38. wyraz ciagu Fibonacciego=39088169
Wprowadz liczbe: 40
40. wyraz ciagu Fibonacciego=102334155
Wprowadz liczbe: 42
42. wyraz ciagu Fibonacciego=267914296
Wprowadz liczbe: 44
44. wyraz ciagu Fibonacciego=701408733
Wprowadz liczbe: 46
46. wyraz ciagu Fibonacciego=1836311903
Wprowadz liczbe: 48
48. wyraz ciagu Fibonacciego=512559680
Wprowadz liczbe: -1
Zla wartosc – koniec obliczen
Programy rekurencyjne są najczęściej krótkie i zwięzłe, poniewaŜ w pewnym sensie odzwierciedlają
naturalny sposób ludzkiego myślenia. Jednak mają teŜ powaŜne wady: są złoŜone obliczeniowo i wykorzystują duŜo pamięci operacyjnej.
Przy kaŜdym wywołaniu jakiejś procedury na stos trafia adres spod którego nastąpiło jej wywołanie, wszystkie parametry i zmienne lokalne. Jeśli wywołamy procedurę raz, nie ma to takiego znaczenia, ale W12 - 23
Podstawy Programowania – Wykład 12
w programach rekurencyjnych moŜe się to zdarzyć na przykład milion razy. Jeśli pamięć zarezerwowana na stos będzie niewystarczająca, na ekranie ujrzymy komunikat "Stack overflow error".
Na szczęście prawie zawsze moŜna zmienić procedurę rekurencyjną na wersję iteracyjną. W niektórych przypadkach jest to bardzo opłacalne, w innych tylko komplikuje program.
Obliczanie funkcji potęgowej xk dla x naleŜącego do zbioru liczb rzeczywistych i k naleŜącego do zbioru liczb całkowitych, zdefiniowanej w następujący sposób:
x0=1,
x1=x,
x-k=1/xk dla k>0,
xk=(xk-1)*x dla k>0 i nieparzystych,
xk=(xk/2)(xk/2) dla k>0 i parzystych
/*rekurencja-potega; przyklad W12P07*/
#include <stdio.h>
#include <stdlib.h>
double potrek(double x, int n); /*wersja rekurencyjna*/
double potnrk(double x, int n); /*wersja iteracyjna*/
int main()
{
char ciag[20];
int wyk;
double liczba, pot;
puts("REKURENCYJNA I ITERACYJNA FUNKCJA POTEGOWA");
do
W12 - 24
Podstawy Programowania – Wykład 12
{
printf("Wprowadz liczbe: ");
gets(ciag);
liczba=atof(ciag);
printf("Wprowadz wykladnik: ");
gets(ciag);
wyk=atoi(ciag);
if(wyk%2)
pot=potnrk(liczba,wyk);
else
pot=potrek(liczba,wyk);
if(liczba==0.0 && wyk==0)
printf("(%.2e)^(%d)=nieokreslona wartosc\n", liczba,wyk);
else
printf("(%.2e)^(%d)=%.2e\n", liczba, wyk, pot);
}
while(!(liczba==0.0&&wyk==0));
}
double potrek(double x,int n)
{
double z;
int i;
printf("Adres i=%u\n", &i);
if(n<0)
return(potrek(1.0/x,-n));
else
if(n==0)
return(1.0);
else
W12 - 25
Podstawy Programowania – Wykład 12
if(n==1)
return(x);
else
if(n%2)
return(x*potrek(x,(n-1)));
else
{
z=potrek(x,n/2);
return(z*z);
}
}
double potnrk(double x, int n)
{
double f=x;
int i,j=(n>0)?n:-n;
if(n==0)
return(1.0);
for(i=1;i<j;i++)
f*=x;
if(n>0)
return(f);
else
return(1.0/f);
}
REKURENCYJNA I ITERACYJNA FUNKCJA POTEGOWA
Wprowadz liczbe: 2.0
Wprowadz wykładnik: 0
Adres i=2293468
W12 - 26
Podstawy Programowania – Wykład 12
(2.00e+000)^(0)=1.00e+000
Wprowadz liczbe: 2.0
Wprowadz wykładnik: -2
Adres i=2293468
Adres i=2293420
Adres i=2293372
(2.00e+000)^(-2)=2.50e-001
Wprowadz liczbe: 8.0
Wprowadz wykładnik: -1
(8.00e+000)^(-1)=1.25e-001
Wprowadz liczbe: 3.0
Wprowadz wykładnik: 5
(3.00e+000)^(5)=2.43e+002
Wprowadz liczbe: 2.0
Wprowadz wykładnik: 6
Adres i=2293468
Adres i=2293420
Adres i=2293372
Adres i=2293324
(2.00e+000)^(6)=6.40e+001
Wprowadz liczbe: 0
Wprowadz wykładnik: 0
Adres i=2293468
(0.00e+000)^(0)=nieokreslona wartosc
Wielomiany Legendre’a mają własność ortogonalności w przedziale <-1,1> i wyraŜają się zaleŜnością
rekurencyjną:
W12 - 27










Podstawy Programowania – Wykład 12
PoniŜej wymieniono kilka początkowych wielomianów Legendre'a:
W12 - 28
Podstawy Programowania – Wykład 12
/*rekurencja-wielomian Legendre'a; przyklad W12P08*/
#include <stdio.h>
#include <stdlib.h>
float legrek(float x,int n); /*wersja rekurencyjna*/
float legnrk(float x,int n); /*wersja iteracyjna*/
W12 - 29
Podstawy Programowania – Wykład 12
int main()
{
char ciag[20];
int rzad;
float leg,x;
puts("WIELOMIAN LEGENDRE'A (REKURENCJA & ITERACJA)");
do
{
printf("Wprowadz rzad wielomianu: ");
gets(ciag);
rzad=atoi(ciag);
if(rzad>=0)
{
printf("Wprowadz zmienna x: ");
gets(ciag);
x=atof(ciag);
if(rzad%2)
leg=legrek(x,rzad);
else
leg=legnrk(x,rzad);
printf("Legendre%d(%.2e)=%.2e\n", rzad, x, leg);
}
}
while(rzad!=-10);
}
float legrek(float x,int n)
{
int i;
printf("Adres i=%u\n", &i);
W12 - 30
Podstawy Programowania – Wykład 12
if(n==0)
return(1.0);
else
if(n==1)
return(x);
else
return(((2*n-1)*x*legrek(x,n-1)-(n-1)*legrek(x,n-2))/n);
}
float legnrk(float x,int n)
{
float p0, p1=1, p2=x;
int i;
if(n==0)
return(1.0);
else
if(n==1)
return(x);
else
{
for(i=2;i<=n;i++)
{
p0=p1;
p1=p2;
p2=((2*i-1)*x*p1-(i-1)*p0)/i;
}
return(p2);
}
}
W12 - 31
Podstawy Programowania – Wykład 12
WIELOMIAN LEGENDRE’A (REKURENCJA & ITERACJA)
Wprowadz rzad wielomianu: 2
Wprowadz zmienna x: 1
Adres i=2293492
Adres i=2293460
Adres i=2293460
Legendre2(1.00e+000)=1.00e+000
Wprowadz rzad wielomianu: 3
Wprowadz zmienna x: 2
Legendre3(2.00e+000)=1.70e+001
Wprowadz rzad wielomianu: 4
Wprowadz zmienna x: -0.5
Adres i=2293492
Adres i=2293460
Adres i=2293428
Adres i=2293396
Adres i=2293396
Adres i=2293428
Adres i=2293460
Adres i=2293428
Adres i=2293428
Legendre4(-5.00e-001)=-2.89e-001
Wprowadz rzad wielomianu: 5
Wprowadz zmienna x: 2
Legendre5(2.00e+000)=1.86e+002
Wprowadz rzad wielomianu: -10
W12 - 32
Podstawy Programowania – Wykład 12
Funkcja Ackermanna została odkryta przez Wilhelma Ackermanna w 1928 roku. Cechą charakterystyczną tej dwuargumentowej funkcji matematycznej, będącej prostym przykładem funkcji rekurencyjnej (co moŜna udowodnić!), jest jej nadzwyczaj szybki wzrost.
Funkcja Ackermanna nadaje się do testowania zdolności kompilatorów do optymalizacji kodu wynikowego.
W jej przypadku dobra optymalizacja moŜe oznaczać skrócenie czasu wykonywania programu o trzy lub cztery rzędy wielkości.
Matematyczna definicja funkcji opisana jest wzorem (m,n – liczby naturalne):
A(m,n)=A(m-1,A(m,n-1))
dla m>0 i n>0
A(0,n)=n+1
dla n>0
A(m,0)=A(m-1,1)
dla m>0
Tab. Wartości funkcji Ackermanna
m\ n
0
1
2
3
4
n
0
1
2
3
4
5
n + 1
1
2
3
4
5
6
2 + (n + 3) − 3
2
3
5
7
9
11
2 * (n + 3) − 3
3
5
13
29
61
125
2(n + 3) − 3
4
13
65533
265536−3
A(3,265536 −3)
A(3,A(4, 3))
b. duŜa licz.
5
65533
A(4,65533)
A(4,A(5,1))
A(4,A(5,2))
A(4,A(5,3))
b. duŜa licz.
6
A(5,1)
A(5,A(5,1))
A(5,A(6,1))
A(5,A(6,2))
A(5,A(6,3))
b. duŜa licz.
Wartość A(4,2) (>10^10^1019000) jest (podobno!) wię ksza niŜ liczba czą steczek we wszechś wiecie
podniesiona do dwusetnej potę gi.
W12 - 33
Podstawy Programowania – Wykład 12
Trudności w obliczeniu wartości funkcji Ackermanna powodowane są głównie złoŜonością wywołań
rekurencyjnych, które łatwo mogą doprowadzić do przepełnienia stosu. Podczas wykonywania obliczenia dochodzi wielokrotnie do wywołań funkcji z identycznymi argumentami (patrz na poniŜsze przykłady).
Odpowiednia implementacja funkcji moŜe wykorzystać powtarzające się instrukcje do znacznego skrócenia procesu obliczania. Przykładowo wartość a(2,0)=3, a a(1,2)=4 co daje się odczytać z tabelki. Poza tym, np. a(2,1)=a(1,3)=a(0,4). RóŜnica polega na tym, Ŝe ostatnia kombinacja argumentów moŜe zostać
obliczona w czasie krótszym (1 wywołanie funkcji) niŜ druga (8 wywołań funkcji) lub pierwsza (14 wywołań).
/*rekurencja-funkcja Ackermanna; przyklad W12P09*/
#include <stdio.h>
#include <stdlib.h>
int acker(int m, int n);
int main()
{
int p1,p2,a;
char ciag[20];
puts("REKURENCYJNA FUNKCJA ACKERMANNA");
do
{
printf("Wprowadz m: ");
gets(ciag);
p1=atoi(ciag);
printf("Wprowadz n: ");
gets(ciag);
p2=atoi(ciag);
a=acker(p1,p2);
if(a==-1)
printf("ackermann(%d,%d)<=niepoprawne parametry\n", p1, p2);
W12 - 34
Podstawy Programowania – Wykład 12
else
printf("ackermann(%d,%d)=%d\n", p1, p2, a);
}
while(p1!=-10);
}
int acker(int m, int n)
{
int a;
printf("**wywolanie acker(%d,%d)\n", m, n);
if((m<0)||(n<0))
a=-1;
else
if(m==0)
a=n+1;
else
if(n==0)
a=acker(m-1,1);
else
a=acker(m-1,acker(m,n-1));
printf("--powrot z acker(%d,%d)=%d\n", m, n, a);
return(a);
}
REKURENCYJNA FUNKCJA ACKERMANNA
Wprowadz m: -3
Wprowadz n: 3
**wywolanie acker(-3,3)
--powrot z acker(-3,3)=-1
ackermann(-3,3)<=niepoprawne parametry
W12 - 35
Podstawy Programowania – Wykład 12
Wprowadz m: 0
Wprowadz n: 8
**wywolanie acker(0,8)
--powrot z acker(0,8)=9
ackermann(0,8)=9
Wprowadz m: 1
Wprowadz n: 2
**wywolanie acker(1,2)
**wywolanie acker(1,1)
**wywolanie acker(1,0)
**wywolanie acker(0,1)
--powrot z acker(0,1)=2
--powrot z acker(1,0)=2
**wywolanie acker(0,2)
--powrot z acker(0,2)=3
--powrot z acker(1,0)=3
**wywolanie acker(0,3)
--powrot z acker(0,3)=4
--powrot z acker(1,2)=4
ackermann(1,2)=4
Wprowadz m: -10
Wprowadz n: 0
**wywolanie acker(-10,0)
--powrot z acker(-10,0)=-1
ackermann(-10,0)<=niepoprawne parametry
W12 - 36
Podstawy Programowania – Wykład 12
Legenda, napisana przez Eduarda Lucas - francuskiego matematyka (1842-1891), który w roku 1883 wymyś lił
grę zwaną WieŜ e Hanoi: "W Indiach, w mieś cie Banares, pod kopułą głównej ś wią tyni, w miejscu, gdzie
znajduje się ś rodek Ziemi, postawił Brahma na brą zowej tabliczce trzy diamentowe pałeczki o wysokoś ci
jednego łokcia i o gruboś ci tułowia pszczoły. Przy stworzeniu ś wiata na jedną z tych pałeczek nanizane
zostały 64 krąŜ ki z czystego złota z otworami poś rodku tak, iŜ utworzyły postać ś cię tego stoŜ ka. Kapłani
luzują c się wzajemnie dniem i nocą bez przystanku, zaję ci są przeniesieniem tego stoŜ ka z pierwszej pałeczki
na trzecią , posiłkują c się przejś ciowo pałeczką drugą , przy czym zobowią zani są najsurowiej przestrzegać
dwu nastę pują cych zakazów: po pierwsze, za jednym uję ciem nie przenosić nigdy wię cej ponad jeden
krąŜ ek; po wtóre, nigdy nie kłaść krąŜ ka wię kszego na mniejszym. Gdy kapłani, zachowują c ś ciś le te
przepisy, ukoń czą swą pracę , nastą pi koniec ś wiata..."
Szczepan Jeleń ski - Lilavati, Rozrywki
/*rekurencja-wieŜe Hanoi; przyklad W12P10*/
#include <stdio.h>
#include <stdlib.h>
void hanoi(int n, int skad, int dokad, int przez);
int main()
{
int lk;
char ciag[10];
puts("WIEZE HANOI - WERSJA REKURENCYJNA");
do
{
printf("Wprowadz liczbe krazkow: ");
gets(ciag);
lk=atoi(ciag);
if(lk>0)
hanoi(lk,1,3,2);
W12 - 37
Podstawy Programowania – Wykład 12
}
while(lk!=-10);
}
void hanoi(int n, int skad, int dokad, int przez)
{
if(n>0)
{
hanoi(n-1,skad,przez,dokad);
printf("przesunac jeden krazek z %d na %d\n", skad, dokad);
hanoi(n-1,przez,dokad,skad);
}
}
WIEZE HANOI – WERSJA REKURENCYJNA
Wprowadz liczbe krąŜków: -3
Wprowadz liczbe krąŜków: 0
Wprowadz liczbe krąŜków: 1
przesunac jeden krazek z 1 na 3
Wprowadz liczbe krąŜków: 2
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 2 na 3
Wprowadz liczbe krąŜków: 3
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 3 na 2
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 2 na 1
przesunac jeden krazek z 2 na 3
przesunac jeden krazek z 1 na 3
W12 - 38
Podstawy Programowania – Wykład 12
Wprowadz liczbe krąŜków: 4
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 2 na 3
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 3 na 1
przesunac jeden krazek z 3 na 2
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 2 na 3
przesunac jeden krazek z 2 na 1
przesunac jeden krazek z 3 na 1
przesunac jeden krazek z 2 na 3
przesunac jeden krazek z 1 na 2
przesunac jeden krazek z 1 na 3
przesunac jeden krazek z 2 na 3
Wprowadz liczbe krąŜków: -10
/*wieze Hanoi-wersja iteracyjna;przyklad W12P11*/
#include <stdio.h>
#include <stdlib.h>
int a[10][4], gora[4];
void hanoi(int n);
void przenies(int z, int na);
int main()
{
int lk;
char ciag[10];
W12 - 39
Podstawy Programowania – Wykład 12
do {
printf("Wprowadz liczbe krazkow: ");
gets(ciag);
lk=atoi(ciag);
if(lk>0)
hanoi(lk);
}
while(lk!=-10);
}
void przenies(int z, int na)
{
printf("Przenies %d z %d na %d\n", a[gora[z]][z], z, na);
gora[na]=gora[na]+1;
a[gora[na]][na]=a[gora[z]][z];
gora[z]=gora[z]-1;
}
void hanoi(int n)
{
int i,j,k,maly;
for(i=n; i>=1; i--)
a[n-i+1][1]=i;
gora[1]=n;
for(i=1;i<=3;i++)
a[0][i]=n+1;
gora[2]=0;
gora[3]=0;
maly=1;
do
W12 - 40
Podstawy Programowania – Wykład 12
{
i=maly+1;
if(i>3) i=i-3;
przenies(maly,i);
maly=i;
if(gora[i]!=n)
{
j=i+1;
if(j>3) j=j-3;
k=i+2;
if(k>3) k=k-3;
if(a[gora[j]][j]<a[gora[k]][k]) przenies(j,k);
else przenies(k,j);
}
}
while(!((gora[1]==n)||(gora[2]==n)||(gora[3]==n)));
}
Referencje
http://binboy.sphere.pl/index.php?show=140
http://www.ia.pw.edu.pl/~stachurs/w3.pdf
http://projekty.pc.pl/materialy/ajd/wi/wiw06.pdf
http://www.neurosoft.edu.pl/jbartman/AiSD_w3_Rekurencja.pdf
http://wipos.p.lodz.pl/zylla/games/hanoi-expl.html
http://chemeng.p.lodz.pl/zylla/games/hanoi5p.html
http://wipos.p.lodz.pl/zylla/games/hanoi-iter1.html
http://pl.wikipedia.org/wiki/Wielomiany_Legendre'a
http://pl.wikipedia.org/wiki/Funkcja_Ackermanna
W12 - 41