Proměnné, výrazy, základní matematické funkce

Vyčíslení vzorečku

Jak přesvědčíme počítač, aby nám vyřešil primitivní číselnou úlohu:

O kolik si zkrátím cestu, když půjdu přes fotbalové hřiště úhlopříčkou místo po obvodu?

Ano, jde o to spočíst

\(a+b-\sqrt{a^2+b^2}\)

pro normované rozměry fotbalového hřiště.

V jazyce Python, který budeme používat na naší přednášce to dokážeme třemi řádky

[1]:
a = 105
b = 68

print( a + b - (a**2 + b**2)**0.5 )
47.9040368357156

Tento krátký program obsahuje

  • tři příkazy (dva přiřazovací (=) a jeden pro textový výstup (print))

  • dvě proměnné (a a b)

  • pět číselných konstant (tzv. literálů) - 105, 68, 2, 2, 0.5

  • jeden numerický výraz zapsaný pomocí operací +, -, * a **

Povšimněte si, že jsme záměrně použili proměnné, místo abychom konstanty opakovali, např.

print( 105 + 68 - (105**2 + 68**2)**0.5 )

Python není jediný programovací jazyk. Pro zajímavost porovnejme tento program s ekvivalentním programem v jazyce C:

#include <stdio.h>
#include <math.h>

int main() {
    double a = 105;
    double b = 68;

    printf("%g\n", a+b-sqrt(a*a+b*b));

    return 0;
}

Pozorování: přívětivější zápis kódu (ale i další důvody) učinily z Pythonu dnes nejpopulárnější jazyk. Stručnost kódu ale naznačuje, že se něco může odehrávat skryto očím nepoučeného uživatele tohoto jazyka. Na našem kurzu na to budeme často muset dávat pozor.

Další problém:

Jak moc se blíží tyto rozměry zlatému řezu?

[4]:
print(a/b, (a+b)/a )
1.5441176470588236 1.6476190476190475

Pozorujeme, že

  • hodnoty proměnných zůstaly v našem prostředí zachovány i po skončení vyhodnocování předchozí buňky

Proměnné

Slouží k uchování dat pro další použití. Jaké proměnné použijeme závisí konkrétním algoritmu/výpočtu. To uvidíme v dalších hodinách.

Při zápisu složitějších matematických výrazů je vhodné delší nebo opakující se podvýrazy uložit do pomocných/dočasných proměnných.

\[f = \frac{1-\sqrt{1-x^2}}{1+\sqrt{1-x^2}}\]

tedy můžeme rozdělit na dva přiřazovací příkazy

odmocnina = (1-x**2)**0.5
f = (1-odmocnina)/(1+odmocnina)

V Pythonu se mají proměnné zapisovat malými písmeny (+podtržítky), tedy místo

Ekin = 0.5*m*v**2

bychom měli např. psát

e_kin = 0.5*m*v**2

Uvidíte, že ne vždy se toto pravidlo dodržuje.

Zápis čísel

Součástí kódu jsou různé hodnoty. Pravidla, která používáme snadno uhodnete z následujících případů.

cele_cislo1 = 123
cele_cislo2 = 1000000
cele_cislo3 = 1_000_000         #  čitelnější zápis

realne_cislo1 = 123.45
realne_cislo2 = 0.0101
realne_cislo3 = 6.02214076e23   #  Avogadrova konstanta
realne_cislo4 = 6.62607015e-34  #  Planckova konstanta

Platí, že pokud se v zápisu čísla vyskytuje . (tečka) nebo e (velké nebo malé), jde o reálné číslo. S jejich vlastnostmi se seznámíme podrobněji později, již teď je rozumné vědět, že přesnost výpočtů s reálnými čísly je asi 15 cifer, zatímco operace probíhající výhradně s celými čísly nemají omezenou přesnost.

Je velmi pohodlné, že Python umí komplexní čísla.

komplexni_cislo1 = 2+3j
komplexni_cislo2 = 0.2-1.3j

Vždy je chápe jako dvojici čísel reálných (tedy i 1+1j), jak si takové tvrzení ověřit se naučíme zanedlouho. Pozor, 1+j není zápis čísla ale součet jedničky a proměnné j.

Pojmenované konstanty

Za konstantu můžeme považovat hodnotu, která

  • Nejméně po dobu běhu programu nemění svoji hodnotu

Konstantou tedy může být jak číslo \(\pi\), tak např. konkrétní rozměry nějakého zařízení, ačkoli v sousední laboratoři mají větší.

Konstanta může být reprezentována konkrétní číselnou hodnotou, např. 105. Je ale nepohodlné a asi i nesprávné mít tuto hodnotu roztroušenou na různých místech kódu. Proto je zvykem tuto hodnotu označit slovy, definovat konstantu

STRANA_A = 105

a dále místo čísla 105 používat identifikátor STRANA_A.

Bývá zvykem, že

  • Hodnoty konstant nelze měnit

To v Pythonu ale neplatí, tento jazyk předpokládá neomylného programátora. Ten se drží pravidla

  • Hodnoty konstant nesmíme měnit

Zvyk/pravidlo v Pythonu toto řeší tak, že proměnné s významem konstant se mají psát velkými písmeny, proměnné malými písmeny. Opět, ne vždy to budeme dodržovat, už jen proto, že rozdělení na konstanty (jen velká písmena) a proměnné (malá první písmena identifikátoru, libovolná další) není často úplně jednoznačné.

Shrnutí:

V programování existují dva koncepty: proměnná a konstanta.

  • Proměnné postupně nabývají různých hodnot, např. proměnná soucet může pro řadu \(1,2,3,..10\) nabývat hodnot \(0,1,3,6,...,55\). Měly by se psát malými písmeny.

  • Konstanty mají jednu pevně danou hodnotu. Měly by se psát velkými písmeny. Ani jedno ale jazyk Python nevynucuje. V mnoha případech se psaní velkými písmeny nedodržuje, např. \(\pi\) se jemnuje math.pi.

Detailně je to popsáno v textu Python Constants

Aritmetické výrazy

Výrazy vnikají složením operandů a aritmetických operátorů. Ty nejdůležitější shrnuje tato tabulka

zápis

hodnota

komentář

1 + 1

2

2 * 3

6

13 / 5

2.6

13 // 5

2

celočíslené dělení

13 % 5

3

zbytek po -"-

5**3

125

mocnění

V ní používáme celočíselné operandy. Kromě celých čísel máme po ruce i čísla reálná (zvaná float) a čísla komplexní.

zápis

hodnota

komentář

6.6743e-11

\(6.6743\times 10^{-11}\)

(lze psát i 6.6743E-11)

(1+1j)**2

\(2i\)

protože \((1+i)^2=2i\)

Reálná (i komplexní) čísla mají narozdíl od čísel celých omezený počet cifer (přesnost)

>>> print(1+0.1-1)
0.10000000000000009

Logické výrazy

Při rozhodování, jak při běhu programu postupovat budeme potřebovat logické výrazy.

zápis

hodnota

komentář

1+1 == 2

True

jedno rovnítko nestačí

1+1 != 3

True

viz symbol \(\ne\)

1 < 2

True

1 > 2

False

1 <= 2

True

1 >= 2

False

Tato porovnání kombinujeme pomocí and, or a not

>>> 1>2 or 3>2
True

>>> 1>2 and 3>2
False

>>> 1<2 and not 3<2
True

Priorita aritmetických operací

Operace, se kterými jsme seznámili, mají takto uspořádané přednosti:

**
*, /, //, %
+, -
<, <=, >, >=, !=, ==
not
and
or

Proto v následujícím výrazu se operace vyhodnocovat od konce, ačkoli pokud prefence operací nevyžadují jinak, vyhodnocují se výrazy z leva doprava.

>>> False or True and not 1 == 19 - 2 * 3**2
False

Samozřejmě, k úpravě pořadí operací se používají závorky (1+2)*3.

  • sčítání + a odčítání - má prioritu nejmenší

  • následuje násobení * a dělení /

  • mocnění ** má mezi binárními operacemi prioritu nejvyšší

  • k úpravě pořadí vyhodnocování operací se používají závorky

Pozn. Všimněte si závorek níže:

Úloha: Jaké jsou tedy správné rozměry hřiště se stejnou plochou, ale správným poměrem stran?

[12]:
phi = (1+5**0.5)/2

p = (a*b*phi)**0.5
q = p/phi

print(p, q, p/q, (p+q)/p)
107.48377868159572 66.42862846449752 1.618033988749895 1.618033988749895

Pro pořádek si zde uvedeme úplnější dokumentaci priority všech operací, které v kurzu potkáme.

  • Vyhodnocování podvýrazu v závorkách ( ... ) nenaruší žádná operace před nebo za závorkami.

  • ** představuje operaci mocnění. Její vysoká priorita umožňuje, že 3*2**2*5==60. Asociativita je zprava, tedy a**b**c == a**(b**c). Ovšem i když máme dispozici operaci mocnění, i v Pythonu je Hornerovo schéma TODO:REF pro vyhodnocovaní polynomu rychlejší!

  • -a představuje tzv. unární mínus. Podobně můžeme psát +a. (Zájemci mohou prozkoumat též unární binární negaci celých čísel, např. ~6.)

  • Násobení zapisujeme *. Dělení / dá i dvou celých čísel podíl jako číslo reálné, zatímco celá část podílu se zapisuje // (pozor, zatímco 5//4==1, je -5//4==-2, tedy "zaokrouhleno" směrem dolů. Pokud bychom po celočíselném dělení \(i/j\) požadovali (anti-)symetrii při \(i\to -i\), použijeme výraz int(i/j)). Zbytek po dělení se zapisuje %. Funguje to i pro reálné operandy, např. print(5.75//2.0, 5.75%2.0) vypíše 2.0 1.75.

  • Následuje sčítání + a odčítání -, nižší prioritu už budou mít jen bitové a logické operace:

  • Jen výjimečně potkáme operace bitového posunu vpravo a>>n (což je rovno a // 2**n) a vlevo a<<n (== a * 2**n).

  • Další bitové operace jsou (vyjmenovány s klesající prioritou) a&b, a^b, a|b. Jejich použití je dost výjimečné, ale např. pro celá čísla \(k\) je 1-(k&1)*2 rychlejší variantou (-1)**k.

  • Teprve nyní následují operace porovnání a>b, a>=b, a<b, a<=b, a==b, a!=b, ta poslední s významem \(a \ne b\). Stejnou prioritu má in v testu 2 in [1,2,3] resp. x not in [1,2,3] a konečně i test identity is a is not, který mj. umožňuje zjistit, zda se dvě proměnné aktuálně odkazují na tatáž data testem x is y.

  • Konečně s klesající prioritou tu máme operátory not, and, or, což umožňuje psát přirozeně p==0 or q!=0 and r/(q*p)>1 bez závorek okolo jednotlivých porovnání.

  • Pro úplnost uveďme, že pokud bychom na if a else v podmíněném výraze nahlíželi jako na operátory, pak mají ještě nižší prioritu: math.sin(x)/x if x!=0 else 1.0.

Pozor, logické operace and, or podléhají zkrácenému vyhodnocování, jak je popsáno níže.

Průběh vyhodnocování výrazů

Výpočet výrazu probíhá zleva doprava, přičemž pokud to priorita operátorů vyžaduje, vyzvednuté hodnoty se uschovají dokud je nebude možno použít. To je důležité, když ve výrazu budeme volat funkce, protože to určuje pořadí jejich volání. Zde si to ilustrujeme pomocí funkce input, která se ptá na vstup z klávesnice. Ještě potřebujeme vědět, že pro řetězce se násobením myslí '2'*3 == '222' a operací plus jejich spojování '1'+'222' == '1222'. Vyzkoušejte si to pro jiné zadané hodnoty.

input("x=") + input("y=") * int( input("z=") )
x=1
y=2
z=3
'1222'

Vidíme, že postupně se vyhodnotí všechny operandy a to zleva doprava a to i tehdy, když je nejprve vzhledem k prioritě operací spočíst pravější část výrazu. Z tohoto pravidla je ale výjimka:

Neúplné vyhodnocování logických výrazů

Zatímco pro určení operace součtu dvou čísel a+b musíme k tomu, abychom určili výsledek, vyhodnotit oba operandy, logické operace se tatko nechovají, protože u operací and, or je v polovině případů znám výsledek operace již po vyhodnocení prvního operandu. To proto, že např. True or x dá stejný výsledek pro obě logické hodnoty x. Při výpočtu se proto hodnota x vůbec nezjišťuje a výraz True or x nabývá hodnoty True pro jakékoli x. Proto True or "nesmysl"/0 nezpůsobí žádnou chybu!

Mohlo by se zdát, že se tak děje kvůli urychlení výpočtu, nicméně povaha logických výrazů často vede k tomu, že zkrácené vyhodnocování logických výrazů vítáme hlavně proto, že nám umožní neřešit rizikové chování výrazů:

if x>0 and y>0 and x**0.5+y**0.5<1:
    ...

Takto psaná podmínka nemá diky zkrácenému vyhodnocování logických výrazů potíže s vyhodnocováním pro záporná x.

Dalším místem, kde o této vlastnosti logických binárních operací and a or musíme vědět jsou výrazy obsahující funkce. Podle příslušných pravidel totiž nebudou příslušné části výrazů vyhodnocovány a funkce v nich obsažené se nezavolají. Lze toho využít k urychlení výpočtu ale může nás to i překvapit, pokud daná funkce má za úkol ještě něco jiného, než vrátit vypočtenou hodnotu.

Rozmyslete si, proč se ve druhém případě Python neptá na hodnotu y:

input("x=") == "1" and input("y=") == "2"
x=1
y=2
True

Tedy došlo k vyhdonocení obou podvýrazů ve tvaru input(...) == .... Naopak, pokdu níže zadáme jako první jinou odpověď než 1, druhý podvýraz input("y=") == "2" se nebude vyhodnocovat na na y se nás program nezeptá.

input("x=") == "1" and input("y=") == "2"
x=2
False
[5]:
# Zde si vyzkoušejte příklady výše
input("x=") + input("y=") * int(input("z="))
[5]:
'1222'

Operandy

Operace, které jsme zmínili, pracují s jedním nebo dvěma svými operandy. Co přesně se odehraje záleží na typu (obou) operandů. Například + znamená jednou sčítání čísel, pokud jsou oba operandy čísla (např. 40+2), ale u řetězců jde o jejich spojování (např. jmeno+' '+prijmeni). Operandy mohou mít více podob:

  • Hodnoty proměnných. V příkazu x = a + b se sčítání účastní hodnoty proměnných a a b.

  • Číselné konstanty. Z jejich zápisu se určí i jejich typ int, float, complex, např. 2.0 je typu float.

  • Řetězcové konstanty, např. první operand ve výrazu 'MUDr. '  + prijmeni.

  • Výraz - např. u výrazu a**2 + b**2 jsou oběma operandy operace + jiné výrazy. Výraz může ale nemusí být v kulatých závorkách.

  • Hodnoty vrácené nějakou funkcí, např. math.sin(x) + 1. Na konverzi typů lze také nahlížet jako na volání funkce.

  • Další...

Pokud je operandem nějaká proměnná, zůstane její hodnota nezměněna. Pokud je operandem výsledek nějaké funkce nebo binární operace, je tento použit a protože není již potřeba, přestává existovat. Jím zabírané místo uvolněno k dalšímu použití.

Knihovna math

Zatím jsme odmocňování řešili operací \(x^{0.5}\). Odmocnina ale představuje důležitý příklad standardní matematické funkce. Ty jsou ve většině programovacích jazyků dostupné přímo nebo ve vhodné knihovně. V Pythonu je to knihovna math a odmocnina se jmenuje sqrt. To, že ji chceme používat musíme předem oznámit.

[2]:
import math

a = 105
b = 68

print( a + b - math.sqrt(a**2 + b**2) )
47.9040368357156

Zde je seznam nejdůležitějších funkcí v knihovně math a také dvě, které Python umí i bez knihovny math a to abs a round.

Za povšimnutí stojí mj. funkce atan2(x,y), která počítá úhel průvodiče bodu o kartézkských souřadnicích \([x,y]\) v rozsahu \((-\pi,\pi>\), zatímco \(\arctan(y/x)\) by i pro záporná \(x\) vracel úhly menší než pravé.

[ ]:
# konstanta pi
print( math.pi )

# trigonometrické funkce
print( math.sin(a) )
print( math.cos(a) )
print( math.tan(a) )

print( math.asin(b/a) )
print( math.acos(b/a) )
print( math.atan(b/a) )
print( math.atan2(b,a) )

# hyperbolické funkce
print( math.sinh(a) )
print( math.cosh(a) )
print( math.tanh(a) )

print( math.asinh(a) )
print( math.acosh(a) )
print( math.atanh(b/a) )

# zaokrouhlování
print( math.floor(p) )
print( math.ceil(p) )
print( round(p) )
print( round(p,2) )

# abs
print( abs(q-p) )
print( math.fabs(q-p) )

# exp, log
print( math.exp(p) )
print( math.log(p) )
print( math.log10(p) )
print( math.log10(p)/math.log10(math.e) )

# faktorial
print( math.factorial(12) )

Chtělo by se napsat, že kromě funkcí obsahuje knihovna math i definice konstant. Např. \(\pi\) získáme napsáním

math.pi

Populární vlastnost Pythonu, umožňující měnit věci podle uživatelových představ, ale také znamená, že i tuto hodnotu můžeme změnit:

[14]:
print( math.cos( math.pi ) )

math.pi = 3
# od teď jsme ztraceni, např.
print( math.cos( math.pi ) )

# takže to hned opravíme
math.pi = 3.1415926535897932385
print( math.cos( math.pi ) )
-1.0
-0.9899924966004454
-1.0

Pokud není z názvu jasné, co funkce počítá, lze si v interaktivním prostředí požádat o nápovědu zapsáním otazníku. Bohužel, její interpretace vyžaduje někdy například tušit, že slovo Integral zde znamená "celé číslo", nikoli pojem z matematické analýzy:

[33]:
math.ceil?
Docstring:
ceil(x)

Return the ceiling of x as an Integral.
This is the smallest integer >= x.
Type:      builtin_function_or_method

S pomocí uvedených funkcí lze vyčíslit běžné výrazy, např.

\[\ln\left(x+\sqrt{1+x^2}\right)\]
[9]:
x = math.sinh(2)

print(x, math.log(x+math.sqrt(1+x**2)))
3.626860407847019 2.0

Poznámka:

Pokud vám knohovna math nestačí je tu ještě scipy.special

[11]:
import scipy.special

print(scipy.special.gamma(10.0), math.factorial(9))
362880.0 362880

Zabudované funkce

Takto (builtin) se v Pythonu označují funkce, jenž jsou k okamžitě dispozici při spuštění i bez importu nějaké knihovny. Ačkoli mnohé zatím nebudeme umět požít, je zde vhodné místo, kde se o nich z mínit.

Mezi zabudované funkce patří

  • Nejzákladnější aritmetické funkce abs(x), round(x), pow(x,y) == x**y, max(x,y,...), min(x,y,...), round(x)

  • Funkce type vrací typ svého argumentu. Budeme ji používat při výkladu typů a také ke kontrole argumentů funkcí.

  • Funkce pro práci se seznamy, např. len(a), sum(a), min(a), max(a) vrací ppočet prvků seznamu, jejich součet a minimální/maximální hodnotu prvku.

  • Funkce input(vyzva) vypíše výzvu a prátí řetězec, který zadáme jako vstup na klávesnici.

  • Následující funkce pro práci s řetězci si probereme při výkladu řetězců:

    • Nejdůležitější je len(s) vracející počet znaků řetězce

    • ord('ž') vrací unicode kód jednoznakového řetězce.

    • K ní inverzní je funkce chr(n) vrací znak s daným kódem.

  • Později také probereme konverzi typů, např. když list("ABC") převede řetězec na seznam znaků. Konverzi lze chápat jako volání funkcí. Proto mezi builtin funkcemi najdete bool, complex, int, list, set, str. Funkce repr se chová podobně jako str, jen někdy vrátí podrobnější informace, než ona.

  • Další funkce, viz dokumentace

Kontrolní úlohy

  1. Vyzkoušejte, kolik desetinných míst trefí Stirlingova formule

    \[n! \approx \sqrt{2 \pi n} \left(\frac{n}{e}\right)^n\left(1+\frac{1}{12n}\right)\]

    pro \(n = 15\). (Vyčíslete obě strany a vizuálně porovnejte výsledky, použijte funkci math.factorial a konstantu math.e.)

  2. Nalezněte efektivní povrchovou teplotu Slunce, pokud by to byla koule o poloměru \(6.95700\times10^8\rm m\) vyzařující jako dokonale černé těleso výkonem \(P = \sigma T^4 = 3.83\times 10^{26} \rm W\). Použijte \(\sigma = 6.95700×10^{-8} {\rm W} {\rm m}^{-2} {\rm K}^{-4}\).