Python:

  • ciągi znaków i listy
  • pęlta for (klasyczna i z użyciem enumerate(lista)
  • konwersje typów t (string=>lista, char->liczba),
  • tworzenie listy, operacje na listach
  • preprocesor Sage, typy

In [1]:
pi.n(digits=3)


Out[1]:
3.14

Plan wykładu:

  1. Wstęp: operacje na liczbach w języku python i systemie Sage.
  2. Reprezentacja liczb w komputerze:
    • systemy addytywne i pozycyjne
    • przedstawienie liczb całkowitych w dwójkowym (binarny) systemie liczbowym
    • zmiana systemu liczbowego (ćwiczenia)
    • zapis liczb rzeczywistych
  3. Liczby zmiennoprzeycinkowe
  4. Konsekwencje operacji na liczbach o skończonej prezycji.

       


In [2]:
21==2*10+1


Out[2]:
True

Zapis liczby $21$ jest w systemie pozycyjnym ciagiem znaków (ang. string), "2" i "1".


In [3]:
x = 21
x


Out[3]:
21

In [4]:
str='21'
str


Out[4]:
'21'

wypiszmy te znaki jedem po drugim:


In [5]:
l = ['a','b','c']
i=0
for z in l:
    print i,z 
    i = i+1


0 a
1 b
2 c

In [6]:
l = ['a','b','c']
for i in range(len(l)):
    print i,l[i]


0 a
1 b
2 c

In [7]:
b,c = 1,2
print b,c


1 2

In [8]:
l = ['a','b','c']
for i,z in enumerate(l):
    print i,z


0 a
1 b
2 c

In [9]:
list(enumerate(l))


Out[9]:
[(0, 'a'), (1, 'b'), (2, 'c')]

In [10]:
for b,c in [(0, 'a'), (1, 'b'), (2, 'c')]:
    print b,c


0 a
1 b
2 c

In [11]:
str = '21' 
for l in str:
    print l


2
1

In [12]:
list(reversed(list(str)))


Out[12]:
['1', '2']

In [13]:
for n,d in enumerate(reversed(list(str))):
    print n,d


0 1
1 2

z tych znaków możemy "złożyć" liczbę, stosując wzór: 

$$21 = 2\cdot 10^1 + 1\cdot 10^0. $$


In [14]:
int("a2")


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-14-cb57289d54c6> in <module>()
----> 1 int("a2")

ValueError: invalid literal for int() with base 10: 'a2'

In [15]:
int('12')


Out[15]:
12

In [16]:
l = []
for i in range(5):
    l.append(2**i)
l


Out[16]:
[1, 2, 4, 8, 16]

In [17]:
[2**3 for i in range(5)]


Out[17]:
[8, 8, 8, 8, 8]

In [18]:
sum ( [int(d)*10^(n) for n,d in enumerate(reversed(list(str)))] )


Out[18]:
21

In [170]:



Out[170]:

Weźmy teraz liczbę $n$ będącą licznbą całkowitą w Sage:


In [19]:
n = 21
type(n)


Out[19]:
<type 'sage.rings.integer.Integer'>

Chociaż tego nigdzie nie deklarowaliśmy, $n$ jest objektem Integer a nie typem prostym języka python "int". Magicznie zadziałał za nas preparser w Sage:


In [20]:
preparse('n=21')


Out[20]:
'n=Integer(21)'

Objekt Integer ma wiele użytecznych  metod, np.:


In [21]:
n.


  File "<ipython-input-21-9ae45436400c>", line 1
    n.
      ^
SyntaxError: invalid syntax

In [22]:
n.nbits()


Out[22]:
5

Zmiana reprezentacji - z dwójkowej na dziesiętną.


In [23]:
str=n.binary()
str


Out[23]:
'10101'

Jak możemy znając przedstawienie dziesiętne, obliczyć reprezentacje danej liczby w systemie binarnym?

Oczywiście mamy do tego narzędzia w Sage:


In [24]:
(21).binary()


Out[24]:
'10101'

In [ ]:

Jednak jak można sobie poradzić ich nie mając (lub używając czystego pythona)? 

Możemy do tego wykorzystać dzielenie z resztą i dzielenie modulo (lub innymi słowami znanego ze szkoły dzielenia z resztą). Wystartujmy od naszej liczby. Podzielmy ją z resztą przez 2, a następnie zapiszemy sobie resztę a wynik potraktujemy jako nową liczbę wejsciowa. Kontynuujać aż otrzymamy w wyniku zero, taka procedura da nam listę reszt. Liest ta  będzie reprezentacją binarną liczby wyjsciowej, ale w odwróconej kolejności.  

Na początej jednak wyróbujmy dzielenie z resztą:


In [25]:
print "modulo ",21%2
print "dzielenie ",21/2
print "dzielenie całkowite ", int(21)/int(2)
print "dzielenie z resztą z Sage ", (21).quo_rem(2)


modulo  1
dzielenie  21/2
dzielenie całkowite  10
dzielenie z resztą z Sage  (10, 1)

Zauważmy, że operatory % i /  dadzą nam poprawne wartości dzielenia modulo i części całkowitej z dzielenia tylko jeśli ich argumenty (tzn n i r) będą typami 'int'. W Sage kazda liczba jest automatycznie zamieniana na objekt np. Integer. Są trzy sposoby by obejść to niepotrzebne w tej chwili udogodnienie:

  1. Skorzystać z czytego pythona.
  2. Wykonać cast do "int"
  3. Użyć metody   'quo_rem', która jest niczym innym jak właśnie dzieleniem z resztą.

Poniżej znajduje się  implementacja:


In [ ]:


In [29]:
%%python
n = 23
while n>0:
    n,r = n//2,n%2
    print n,r


11 1
5 1
2 1
1 0
0 1

In [28]:
n = 23
while n>0:
    n,r = int(n)/int(2),n%2
    print n,r


11 1
5 1
2 1
1 0
0 1

In [33]:
n=23
while n>0:
    n,r = n.quo_rem(2)
    print n,r


11 1
5 1
2 1
1 0
0 1

Zamiast na ekran, zapiszmy sobie wynik do tablicy, by można było z niego złożyć reprezentacje binarną liczby.


In [34]:
n=23
n_binary=[]
while n>0:
    n,r = n.quo_rem(2)
    n_binary.append(r)

Złożenie wymaga użycia operacji join, która w pythonie z listy robi ciąg znaków. (warto pamiętać, że odwrotną operacją jest "split").


In [40]:
str="".join([ i.str() for i in reversed(n_binary)])
str


Out[40]:
'10111'

Na koniec sprawdźmy czy wynik jest poprawny, korzystając bezpośrednio z definicji reprezentacji binarnej:


In [41]:
sum ( [int(d)*2^(n) for n,d in enumerate(reversed(list(str)))] )


Out[41]:
23

Czyli jest OK!

Ułamki dziesiętne.

W systemie dwójkowym można przedstawiać również liczby rzeczywiste. Na przykład ułamek dziesiętny o podstawie 2 można zapisać jako:

$$0.101_{2}=0\cdot 2^0 + 1 \cdot 2^{-1} + 0 \cdot 2^{-2} + 1 \cdot 2^{-3} = 0.625_{10}\;.$$

 

Liczby rzeczywiste w Sage są reprezentowane przez klasę reprezentującą liczby rzeczywiste. Jest ona w stanie obslugiwać liczby o dowolnej precyzji oraz posiada wiele użytecznych metod. 


In [42]:
x=1.2
print type (x)
print preparse('x=1.2')


<type 'sage.rings.real_mpfr.RealLiteral'>
x=RealNumber('1.2')

Można m.in. otrzymać reprezentację binarna danej liczby:


In [43]:
(0.625).str(base=2)


Out[43]:
'0.10100000000000000000000000000000000000000000000000000'

W drugą stronę: tworząc liczbę rzeczywistą z ciągu znaków, możemy jawnie podać reprezentację:


In [44]:
x = RealNumber('0.101',base=2)
x


Out[44]:
0.625000000000000

Część całkowitą liczby rzeczywistej możemy uzyskać stosując zarówno funkcję pythonową floor jak i metodę floor:


In [45]:
x=1.2
print "funkcja floor",x,floor(x)
print "metoda floor",x,x.floor()


funkcja floor 1.20000000000000 1
metoda floor 1.20000000000000 1

In [46]:
print "czesc dziesietna",x-floor(x)


czesc dziesietna 0.200000000000000

In [138]:



Out[138]:

Aby wyznaczyć reprezentację dwójkową liczny rzeczywistej, możemy rozbić ją na część całkowitą i dziesiętną. Część całkowitą zamieniamy według znanego algorytmu ma postać dwójkową a z częścią dziesiętną postępujemy inaczej. Mnożymy ją przez 2 i zaposujemy części całkowite wyniku: 


In [47]:
n=0.21
licznik=0
n_binary=[]
while True:
    licznik=licznik+1
    n=n*2 
    r=int(n)
    n=n-floor(n)  
    n_binary.append(r)
    if  n==0 or licznik>20:
        break
n_binary


Out[47]:
[0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1]

In [141]:



Out[141]:

Liczby zmiennoprzecinkowe

http://pl.wikipedia.org/wiki/IEEE_754


In [140]:



Out[140]:


In [90]:
x=.1

In [91]:



Out[91]:
<type 'sage.rings.real_mpfr.RealLiteral'>

In [49]:
sign,mantissa,exponent=x.sign_mantissa_exponent()
sign,mantissa,exponent


Out[49]:
(1, 7205759403792794, -56)

In [50]:
xx=N(5939122008594842*2^(-48))

In [51]:
xx


Out[51]:
21.1000000000000

In [52]:
xx.str(truncate=False)


Out[52]:
'21.100000000000001'

In [53]:
(mantissa*2^exponent).n()


Out[53]:
0.100000000000000

In [ ]:


In [ ]:


In [ ]:


In [54]:
x=0.4

In [55]:
x.str(base=2,truncate=False)


Out[55]:
'0.011001100110011001100110011001100110011001100110011010'

In [ ]:


In [56]:
x.prec()


Out[56]:
53

In [57]:
sign,mantissa,exponent=x.sign_mantissa_exponent()
sign,mantissa,exponent


Out[57]:
(1, 7205759403792794, -54)

In [58]:
(mantissa*2^exponent).n(digits=100)


Out[58]:
0.4000000000000000222044604925031308084726333618164062500000000000000000000000000000000000000000000000

In [78]:



Out[78]:

Przykład arytmetyka pięciobitowa:

Wyobraźmy sobie, że mamy 2-bitową mantysę i  2-bitowy eksponent. Wtedy:

$$1.b_1b_2\times 2^{k-2},$$ gdzie $b_1$ i $b_2$ mogą przybierać wartości $0$ i $1$  a $k$: $0,1,2,3$.


In [59]:
x=RR('1.01',base=2)

In [60]:
print x.str(base=2)
print x.str(base=10)


1.0100000000000000000000000000000000000000000000000000
1.25000000000000

In [61]:
b=12
print "Liczba jest mniejsza niz :%d  lub %d" % (b,b*2)


Liczba jest mniejsza niz :12  lub 24

In [64]:
num_tbl=[[ r'$1.%d%d \times 2^{%d}$' %(b1,b2,k-2),(RR('1.%d%d'%(b1,b2),base=2)*2^(k-1)).exact_rational(),RR('1.%d%d'%(b1,b2),base=2)*2^(k-1)] for b1,b2,k in CartesianProduct([0,1],[0,1],[0,1,2,3])]

num_tbl.sort(key=lambda x:x[2])

# sortowanie tableli przez itemgetter
# from operator import itemgetter
# num_tbl.sort(key=itemgetter(2))


/usr/lib/sagemath/local/lib/python2.7/site-packages/sage/repl/ipython_kernel/__main__.py:1: DeprecationWarning: CartesianProduct is deprecated. Use cartesian_product instead
See http://trac.sagemath.org/18411 for details.
  from ipykernel.kernelapp import IPKernelApp

In [92]:
html.table( num_tbl )


Out[92]:

Cała arytmetyka jest reprezentowana przez zaledwie 16 różnych liczb:


In [66]:
len(num_tbl)


Out[66]:
16

In [67]:
nums=[ i[2] for i in num_tbl]

Narysujmy te liczby na osi liczbowej:


In [68]:
points(zip(nums,[0]*len(nums)),pointsize=25,ticks=[1/4,None]).show(xmin=0,xmax=4,figsize=[10,1] )



In [48]:



Out[48]:

Zbadajmy system 64 bitowych liczb zmiennoprzycinkowych mantysa ze znakiem 53bity + 11 bitów wykładnik:


In [69]:
x=1.0

In [70]:
x.nextabove().str(truncate=False)


Out[70]:
'1.0000000000000002'

In [71]:
x.str(base=2)


Out[71]:
'1.0000000000000000000000000000000000000000000000000000'

In [72]:
x.nextabove().str(base=2)


Out[72]:
'1.0000000000000000000000000000000000000000000000000001'

In [73]:
x.nextbelow().str(base=2)


Out[73]:
'0.11111111111111111111111111111111111111111111111111111'

In [74]:
x.nextbelow().str(truncate=False)


Out[74]:
'0.99999999999999989'

In [75]:
x.nextabove().str(truncate=False)


Out[75]:
'1.0000000000000002'

In [76]:
x.nextbelow().str(base=2)


Out[76]:
'0.11111111111111111111111111111111111111111111111111111'

In [77]:
x.nextabove()-x


Out[77]:
2.22044604925031e-16

In [78]:
x.nextbelow()-x


Out[78]:
-1.11022302462516e-16

In [79]:
print "%e" % 1234


1.234000e+03

In [80]:
x=21

In [81]:
x.str(base=2)


Out[81]:
'10101'

Konsekwencje operacji na liczbach o skończonej precyzji


In [149]:



Out[149]:

Liczba 0.1 nie jest reprezentowana dokładnie!


In [82]:
10 * (1.1 -1) - 1


Out[82]:
8.88178419700125e-16

Należy unikać porównań typu:

if (x == 1.0)
{
....
}

In [83]:
type(1)


Out[83]:
<type 'sage.rings.integer.Integer'>

In [84]:
1/2+1/3


Out[84]:
5/6

In [85]:
sum = 0.0
for i in range(1000000):
    sum += 0.1
print(sum)


100000.000001333

In [86]:
sum = 0.0
for i in range(1000000):
    sum += 0.1
sum


Out[86]:
100000.000001333

In [87]:
var('x')


Out[87]:
x

In [88]:
((x-1)^4).expand()


Out[88]:
x^4 - 4*x^3 + 6*x^2 - 4*x + 1

In [89]:
plot(x^4 - 4*x^3 + 6*x^2 - 4*x + 1,(x,0.9996,1.0004) ) + plot((x-1)^4,(x,0.9996,1.0004),color='red',figsize=5 )


Out[89]:

Liczby zmiennoprzecinkowe na osi rzeczywistej, dla różnych precyzji (wykorzystując Sage - RealNumber).

@interact
def _(p=slider( range(2,10) ) ):
    x=1.
    xf=x.n(prec=p)
    xf_lst=[]
    while xf<=31:
        xf_lst.append(xf)
        xf=xf.nextabove()
    x=1.
    xf=x.n(prec=p)
    while xf>=.1:
        xf=xf.nextbelow() 
        xf_lst.append(xf) 
#        ,ticks=[1/4,None]
    points(zip(xf_lst,[0]*len(xf_lst)),pointsize=25).show(xmin=0,xmax=31,figsize=[15,1] )

In [154]:



Out[154]:


In [96]:



Out[96]: