AST415 Astronomide Sayısal Çözümleme - I

Ders - 02 Formüllerle Hesaplar ve Yazım Kuralları (Syntax)

Doç. Dr. Özgür Baştürk
Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
obasturk at ankara.edu.tr
http://ozgur.astrotux.org

Dikey Atış Problemi

Newton Hareket Kanunları'ndan yararlanarak matematiksel ifadesini bulan bu problemin çözümü, yer çekimine zıt yönde atılan bir cismin düşey konumunun (y) zamana (t) bağlı olarak değişimini aşağdaki formülle verir.

$$y(t) = V_{0} t - \frac{1}{2} g t^{2}$$


Burada $V_{0}$ cismin ilk hızını, $g$ yerçekimi ivmesini $y = 0$ konumu cismin $t = 0$ anındaki konumunu göstermektedir. Cismin tekrar başlangıç konumuna dönmesi için geçen süreyi hesaplamak üzere

$$V_{0} t - \frac{1}{2} g t^{2} = 0 \rightarrow t(V_{0} - \frac{1}{2} g t) = 0$$
$t = 0$ ve $t = \frac{2 V_{0}}{g}$

olduğu düşünülecek olursa $t \in[0, \frac{2V_{0}}{g}]$ olur.

Başa Dön

Değişken Kullanımı

Dikey atış probleminde cismin herhangi bir $t$ anındaki düşey konumunu hesaplayan bir Python programı yazmak istiyor olalım. Programımızı $V_{0}$, $t$ ve $g$'nin farklı değerleri için çalıştırmak programımız üzerinde sürekli değişiklik yapmamız gerekecek. Tüm programlama dillerinde olduğu gibi Python'da da değişkenler kulllanarak bu sorunu aşabiliriz.

In [1]:
v0 = 5 # Ilk hiz m/s
t = 0.6 # zaman s
g = 9.81 # yercekimi ivmesi m/s**2
y = v0*t - 0.5*g*t**2
print(y)
1.2342

Şimdi programımızı $V_{0}$, $t$ ya da $g$'nin farklı değerleri için çalıştırmamız çok kolay. Örnek olarak $V_{0} = 1 m/s$ ve $t = 0.1 s$ için alınan toplam yolu tekrar hesaplayalım.

In [2]:
v0 = 1 # Ilk hiz
t = 0.1 # zaman
y = v0*t - 0.5*g*t**2
print(y)
0.050949999999999995

! g'yi tekrar yazmadığımıza ancak y'yi veren ifadeyi tekrar yazdığımıza dikkat ediniz!

Ayni anda birden fazla degiskeni tek satirda tanimlayabilirsiniz.

In [3]:
x = y = 3 # hem x'e hem y'ye 3 degerini ata
print(x,y)
y = 5
print(x,y) # y degistiginde x'in degismedigine dikkat ediniz
a, b, c = 3.14, 'AST415', 8 # ayni anda birden fazla degisken tek satirda
print(a)
print(b)
print(c)
#a, b, c = 3.14, 'AST415 Astronomide Sayisal Cozumleme', 8, True #degisken sayisi ile deger
#print(c)
#a, b, c = 3.14, 'AST415 Astronomide Sayisal Cozumleme' #sayisinin esit olmadigina dikkat
#print(a)
3 3
3 5
3.14
AST415
8

Değişken İsimlendirmeleri

  • Değişkenleri isimlendirmek üzere (inglizce alfabe dışında karakter içermeyen) küçük (a-z) ve büyük (A-Z), rakamlardan (0-9) ve alt çizgi (– underscore) işaretinden faydalanabilirsiniz.
  • Değişken isimleri rakamla başlayamaz! Bu şekilde verilmiş bir değişken ismi yazım (syntax) hatası verir.
  • Python küçük harflerle büyük harfleri birbirinden ayırır (case sensitive). Yani “x” ile “X” aynı değildir!
  • Kesin bir kural olmamakla birlikte değişkenler için küçük harf, sabitler (PI, PLANCKSABITI gibi) için büyük harf kullanmak, kelimeleri “” (alt çizgi) işaret ile birbirinden ayırmak; daha sonra göreceğiniz sınıf (class) isimlerinde ikinci kelimenin baş harfini büyük yapmak gibi genel Python alışkanlıkları olmakla birlikte bunları takip etmezseniz hata vermez. (Python'da kullanılan genel kod yazma stili alışkanlıkları için bkz: PEP 0008: Style Guide for Python, https://www.python.org/dev/peps/pep-0008/)
  • Mümkün olduğunca açıklayıcı değişken isimleri kullanmak hem siz, hem de kodunuzu okuyanlar (diğer programcılar, BİZLER!) için iyidir. Örnek problemimizde $v0$ yerine $ilk\_hiz$, $t$ yerine $zaman$, $y$ yerine $dusey\_konum$, $g$ yerine $yercekimi\_ivmesi$ kullanirsak daha aciklayici olur
  • Python için saklı kelimeleri değişken ismi olarak kullanamazsınız (and, as, assert, break, class, continue, def, del, elif, else, except, False, finally, for, from, global, if, import, in, is, lambda, None, nonlocal, not, or, pass, raise, return, True, try, with, while, yield). Çok istiyorsanız sonlarına bir “_” işareti koymak işi çözer!

Değişken İsimlendirmeleri'ne Örnekler

Bu derste yapılan en klasik hata, değişken isimleri içerisinde Türkçe karakterler kullanmaktır. Bu dersin ödevleri için yazdığınız kodlar çalışmasa da değerlendirmeye alınacaktır. Türkçe karakter kullanma hatasından kurtulabilmeniz amacıyla ödevlerde kullandığınız (ve bizim de yakaladığımız!) her Türkçe karakter için 2 puanınızı istikrarlı bir şekilde kıracağız! Yine de bilgisayarınızın sistemine ve jupyter defterinizin başına #UTF-8 kodunu ekleyip eklemedğinize bağlı olarak Türkçe karakterler kullanılan değişken isimlendirmelerinin de çalışma ihtimali bulunmaktadır.

In [4]:
başlangıç_hızı = 5
print(başlangıç_hızı)
5

Aşağıdaki değişken isimlendirmelerinin doğruluğunu kontrol ediniz.

  • 1a = 2
  • _ = ((d-45)/c + 32))
  • a1 = b
  • sale = 6%
  • x = 3
  • doviz = 10$
  • y = X + 4
  • eposta = somebody@somewhere.com
  • pi = "3.141"
  • and = "ve"
  • c = 5 ** 2 ** 0.5 ** 3
  • Degisken = x > 0

Başa Dön

Yorum İfadeleri

Programlarınıza kendiniz ve kodunuzu okuyan başkaları (örn. BİZLER!) için yorum satırları koymanız çok yararlıdır. Bunun için Python tarafından çalıştırılmasını istemediğiniz ifadelerin başına “#” işaretini koymanız yeterlidir.

In [5]:
# havaya dogru bir ilk hizla atilan cismin t anindaki 
# yuksekligini hesaplayan program
v0 = 5 # ilk hiz
g = 9.81 # yercekimi ivmesi
t = 0.6 # zaman
y = v0*t - 0.5*g*t**2 # dusey konum
print y
  File "<ipython-input-5-f05b36bdb36a>", line 7
    print y
          ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(y)?

Zaten açıklayıcı değişken isimleri kullandıysanız aynı kelimeleri kullanarak bir de yorum eklemeye gerek yoktur. Aynı şeyi iki kere yapmamak iyi bir programcı alışkanlığıdır!

Başa Dön

Metin ve Sayı Formatlama

printf fonksiyonalitesi

Düşey atış problemi çözen basit programımızın çıktısını sadece alınan toplam yolu rakamsal olarak vermek yerine daha açıklayıcı bir şekilde; örneğin, “t = 0.6 saniyede cismin yuksekligi 1.23 metredir.” gibi bir cümleyle vermek isteyebiliriz. Python programlama dilinde metin formatlamak için sık başvurulan bir yöntem (C programlama dilindeki printf fonksiyonundan miras alındığı şekliyle) yer tutuculara başvurmaktır. Ekrana yazdırılacak metin ifadesi içerisinde bir değişkenin değerinin yer almasının istendiği yere % işareti ile bir yer tutucu konur. Değişkenin değerinin hangi formatta (tam sayılar için d, metinler için s gibi...) yazdırılmasının hedeflendiği bu % işaretinden sonra belirtilir. % işareti ile başlayan bu ifade yer tutucu (ing. placeholder) olarak bilinir. Yer tutucu bir değişkenin metin ifadesi içerisinde yerini tutar. Değişkenin değeri bu ifadenin yerine gelir. Değişkenin ne olduğu ise metin ifadesi tamamlandıktan sonra % işaretini takiben parantez içinde verilir. Tek bir değişkenin yeri tutulduğunda parantez kullanılması bir zorunluluk değildir. Birden fazla değişkenin metnin farklı noktalarında yerleri tutulabilir. Bu değişkenler metnin sonunda parantez içinde aralarına virgül konarak birbirinden ayrılır. Yer tutucular sadece değişkenlerin değil pek çok türden nesne ve değerin de (metin sonrasında ne olduklarının verilmesi kaydıyla) yerini tutabilirler.

Sonuç olarak dikine atış programımızı istenen çıktıyı verecek şekilde aşağıdaki gibi düzenleyebiliriz.

In [ ]:
# havaya bir ilk hizla atilan cismin t anindaki 
# yuksekligini hesaplayan program
v0 = 5 # ilk hiz
g = 9.81 # yercekimi ivmesi
t = 0.6 # zaman
y = v0*t - 0.5*g*t**2 # cismin dusey konumu
# dusey konum
print("t = %g saniyede cismin yuksekligi %.2f metredir" % (t,y))
# buradan sonrasi 10 Ekim 2018 tarihli dersten
# degisken yerine tum ifadeyi de %'den sonra koyabiliriz
print("t = %g saniyede cismin yuksekligi %.4f metredir" % \
      (t,v0*t - 0.5*g*t**2))
# t'yi 0.595 yapip sayinin nasil yuvarlandigini gorelim
t = 0.595
print("t = %g saniyede cismin yuksekligi %.2f metredir" % \
      (t,v0*t - 0.5*g*t**2))
# Tek bir yer tutucu kullanildigi zaman paranteze alma zorunlulugu yoktur
# t'yi degistirdigimize ama y'yi degistirmedigimize dikkat ediniz
print("Cismin yuksekligi %.4f metredir" % y)
# Metnin sonuna basit bir syntax'la nesne degeri yazdirabilirsiniz
print('y = ', y)

Aşağıda en sık kullanılan bazı "YER TUTUCULARI" görüyorsunuz:

%s : Metin değişkenler için yer tutucu
%d : Tamsayı değişkenler için yer tutucu
%0xd : Önüne getirilen “0” rakamları ile birlikte x basamak yer kaplayacak tam sayı yer tutucusu
%f : Noktadan sonra 6 basamak kaplayacak şekilde yazılmak istenen noktalı sayı yer tutucusu
%e : Kompakt bilimsel gösterim yer tutucusu
%E : Kompakt bilimsel gösterim yer tutucusu (üssü belirtmek üzere e yerine E kullanılır)
%g : Reel bir sayı için olası en kompakt yazımı veren yer tutucu
%G : %g ile aynı sadece üs gerektiğinde belirtmek üzere e yerine E kullanılır
%x : Bir tam sayı nesnesini (int) metne (string) dönüştürüp onaltılık (hexadecimal) sistemde göstermek için kullanılır
%% : yüzde (%) işaretini yazdırmak gerektiğinde kullanılır

Bazı formatları denemek üzere ilk programımızı kullanalım. Birden çok satır süren metinler için """ işaretinin kullanıldığına dikkat ediniz.

In [ ]:
v0 = 5
g = 9.81
t = 0.6
y = v0*t - 0.5*g*t**2
print("""
v0=%.3E m/s ilk hizla firlatilan cismin
t=%f saniyedeki yuksekligi
%.2f metredir.
""" % (v0, t, y))

str.format yöntemi

Yine dikey atış problemi çözen basit programımızın çıktısını sadece alınan toplam yolu rakamsal olarak vermek yerine daha açıklayıcı bir şekilde; örneğin, "t = 0.6 saniyede cismin yuksekligi 1.23 metredir." gibi bir cümleyle verelim. Ancak bu kez printf fonksiyonalitesi yerine daha modern bir yöntem olan str.format yöntemini kullanalım. Tıpkı printf yönteminde olduğu gibi daha modern olan bu yöntemde de yer tutuculara (ing. placeholder) başvurulmaktadır. Ancak bu kez % işareti yerine { } işaretleri kullanılır. Metinde yeri tutulan nesneler metnin sonunda, bir metin nesnesi fonksiyonu (attribute) olan format fonksiyonu içinde sıralanır.

Bu yöntemle değişkenlerin konumlarını metin içinden bildirmek mümkün olduğu için, en sonda format fonksiyonunun içinde yeri tutulan değişkenlerin doğru sırada verilmesi de bir zorunluluk olmaktan çıkar.

In [ ]:
print("t = {1:g} saniyede cismin yuksekligi {0:.2f} metredir.".format(y,t))
# siralamayi printf yonteminde oldugu gibi de kullanabiliriz
print("t = {:g} saniyede cismin yuksekligi {:.2f} metredir.".format(t,y))

Bu yöntemde herhangi bir metni hizalamak için "-" ve "+" işaretleri yerine "<" ve ">" işaretleri kullanılır.

In [ ]:
print('%-10s' % ('test')) # printf stili
print('{:<10s}'.format('test')) # str.format yontemi
print('%10s' % ('test'))
print('{:>10s}'.format('test'))

Bu yöntemle eski printf yöntemine göre metin hizalama ve yazdırma üzerinde daha çok kontrol sahibi olmak mümkün hale getirilmiştir.

In [ ]:
print('{:_<10s}'.format('test'))
print('{:^10s}'.format('test'))
print('{:06.2f}'.format(3.141592653589793))
print('{: d}'.format((- 23)))
print('{:+d}'.format(42))
print('{:=+8d}'.format((18)))

Yer tutucular bu formatla isimlendirilebilir ve adlarıyla atıfta bulunularak kullanılabilir.

In [ ]:
print('{birinci} {ikinci}'.format(birinci='Ad', ikinci='Soyad'))
print('{birinci} {ikinci}'.format(ikinci='Soyad', birinci='Ad'))

Bu yöntemle ayrıca format parametreleri parametrik şekilde de kullanılabilir.

In [ ]:
print('{:{hiza}{genislik}}'.format('test', hiza='^', genislik='10'))
print('{} = {:.{hassasiyet}f}'.format('Sayi ', 2.7182, hassasiyet=2))
print('{:{genislik}.{hassasiyet}f}'.format(3.14159, genislik=8, hassasiyet=2))
print('{:{}{}{}.{}}'.format(2.7182818284, '>', '+', 10, 3))
print('{:{}{isaret}{}.{}}'.format(2.7182818284, '>', 10, 3, isaret='+'))

Python 3.6'dan itibaren metin formatlama için daha esnek bir yapıya sahip metin interpolasyonu (string interpolation) ya da f-strings adı verilen bir yöntem de tanıtılmıştır. Ayrıca string modülü fonksiyonları da metin formatlamak için çeşitli olanaklar sunar ancak bu yöntemler bu dersin kapsamı dışında tutulmuştur. Merak edenler Real Python'dan https://realpython.com/python-string-formatting/ daha fazla bilgi edinebilirler.

Başa Dön

f-string yöntemi

Python'a 3.6 versiyonu sonrası eklenen f-string yapısı string formatlamayı daha da kolaylaştırmıştır. Kullanılacağı metnin başına $f$ harfini yazıp str.format yapısında kullanılan syntax'a benzer biçimde ${ }$ işaretleri kullanılarak formatlama yapılır.

In [ ]:
print(f"t = {t} saniyede cismin yuksekligi {y} metredir.")

f-string'leri kodla aynı anda çalıştırıldığı için içlerindeki herhangi bir hesap da eş zamanlı yapılacağından esneklik sağlar.

In [ ]:
f"{2 * 37}"

f-stringler hakkında daha geniş bilgi, esnekliği Python hakkında daha derin bilgi gerektirdiğinden, metin değişikenlerin işlendiği 8. bölümde verilecektir.

Matematiksel İşlemler

Python'da matematiksel işlemler, bu işlemlerde karşılaşılan bazı problemler ve işlem sırası gibi konuları santigrat derece – fahrenheit dönüşümü örneği üzerinden tartışalım.

$$F = \frac{9}{5} C + 32$$

Bu problemi C = 21° için basit bir Python koduyla çözmeye çalışalım.

In [ ]:
C = 56
F = (9 / 5)*C + 32
print(F)

Tam Sayı Bölmesi

Tam sayı bölmesinin davranış şekli Python 2.x versiyonunda (ve Java gibi pek çok başka programlama dilinde) farklıdır. Yukarıdaki örnek Python 2.x'de 53 (9 / 5 = 1 --> 1 * 21 = 21 --> 21 + 32 = 53) tam sayı sonucunu verir. Python 2.x C = 21 ifadesiyle; 21 bir tam sayı olduğu için, bir tam sayı (int) nesnesi oluşturur ve bu nesnenin adı da C olarak belirlenmiş olur. Benzer şekilde C = 21.0 şeklinde bir atama yapılmış olsaydı bir kayan sayı (float) nesnesi oluşturulmuş ve bu nesnenin adı C olarak belirlenmiş olacaktı (21 ≠ 21.0). Python 3.x ise kullanıcıya, sonucun her zaman bir kayan sayı olduğu gerçek bölme yapma olanağı sağlar.

Python 3.x ile birlikte, bölünen sayıların türünden (tam sayı ya da değil) bağımsız olarak, sonucun noktadan sonraki basamaklarının kesilip atıldığı (yuvarlama (rounding) değil kesme (truncation)) yeni bir bölme operatorü ( // ) sağlanmıştır. Bu operatörün sonucu eğer bölünen sayılar birer tam sayı ise bir tam sayıdır (integer). Böylece Python 2.x2'deki tam sayı bölme fonksiyonalitesi // (floor division) operatörü ile devam ettirilmiştir. Eğer bölünen sayılardan en az biri kayan sayı ise sonuç kayan sayı (float) cinsinden ve yine noktadan sonraki basamakları kesilerek verilir.

In [ ]:
x = 9//5
print(x), print(type(x))
y = 9.//5
print(y)
print(type(y))

“Bir Python programı yazmak, nesneler oluşturmak ve bu nesnelerin değerlerini değiştirmektir”

Başa Dön

Aritmetik Operatörlerin Öncelik Sırası

Aslında matematikte olduğundan çok farklı değil. Üs işareti (**) 'nin çarpma (*) ve bölmeye (\/) önceliği var. Sonra toplama (+) ve çıkarma (-) geliyor ve parantez içi ifadeler her zaman daha büyük öncelik taşıyor.

Örnek 1

$$\frac{4}{8} + 2*\frac{3^{4}}{9}$$ $$\frac{4}{8} = 0.5 → 3^{4} = 81 → 2*81 = 162 → \frac{162}{9} = 18 → 0.5 + 18 = 18.5$$

Örnek 2

$$\frac{4}{(6 + 2)} + 2*4^{\frac{4}{8}}$$ $$\frac{4}{(6 + 2)} = \frac{4}{(8)} = 0.5 → 4^{\frac{4}{8}} = 4^{0.5} = 2 → 2*2 = 4 = → 0.5 + 4 = 4.5$$

Şimdi bu işlemleri Python ile yapalım.

In [ ]:
# Ornek 1
print(4 / 8 + 2*3**4/9)
# Ornek 2
print(4/(6+2) + 2*4**(4/8))

Python'da aritmetik operatörlerin sırasını anlamak temel düzeyde matematik bilen herkes için oldukça kolaydır. Matematik derslerinde öğrendiğiniz temel bilgiyi referans almanız yeterli olacaktır.

Başa Dön

Standart Matematiksel Fonksiyonlar

Python'da standart matematiksel fonksiyonların nasıl çalıştığını anlamak için dikey atış probleminde cismin t anındaki yüksekliği (y) yerine, herhangi bir yüksekliğe ne kadar zamanda çıkacağını bulma probleminden hareket edelim.

Bu amaçla

$$y(t) = V_{0} t - \frac{1}{2} g t^{2}$$ $$\frac{1}{2} g t^{2} - V_{0} t + y = 0$$

ikinci derece denklemini çözmemiz gerekir. Bu denklemin çözümü

$$t_{1, 2} = (V_{0} \pm (V_{0}^{2} - 2 g y)^{1/2}) / g$$

sonucunu verir (iki sonuç vardır zira cisim bu yüksekliğe biri yükselirken, diğeri inerken olmak üzere iki kez ulaşır!). Bu işlemi Python ile gerçekleştirmek için karekök (sqrt) fonksiyonuna ihtiyaç duyacağımız açıktır.

Modül Fonksiyonları

Python'da standart fonksiyonları (built-in functions) kullanmak için bir şey yapmanıza gerek yoktur. Daha önce örneklerini gördüğünüz $ print$ fonksiyonu böyle fonksiyonlardandır. Değişken türü değiştirmeye olanak sağlayan $ str$, $ int$ ve $ float$ gibi fonksiyonlar da böyledir.

In [6]:
# Standart (built-in) Python Fonksiyonu Ornekleri
print("Merhaba Dunya!")
x = 5
print(x, type(x))
x = str(x)
print(x, type(x))
x = float(x)
print(x, type(x))
Merhaba Dunya!
5 <class 'int'>
5 <class 'str'>
5.0 <class 'float'>

Karekök gibi trigonometrik, logaritmik, üstel, hiperbolik fonksiyonların da aralarında bulunduğu pek çok matematiksel ifadenin nasıl hesaplanacağı uzun yıllar sonucunda oluşturulmuş algoritmalarla saptanmış ve pek çok programcı bu algoritmalara dayanan fonksiyonlar hazırlamışlardır.

Python'da modül benzer nitelikte işler yapan fonksiyonların bir araya toplandığı programlara denir. Örneğin math modülünde pek çok matemtaik fonksiyonu bulunmaktadır. astropy modülünde ise astronomi hesapları için gerekli (Julyen günü dönüşümü, ufuk yüksekliği hesabı gibi...) pek çok fonksiyon bulunur.

Bir modüldeki fonksiyonları kullanabilmek için öncelikle onu çağırmak (import etmek) gereklidir. Bu işlem en basit şekliyle

import modul_adi

ifadesiyle yapılır.

Daha sonra bu modüldeki fonksiyon (örneğin karekök fonksiyonu sqrt) modul_adi.fonksiyon(arguman) şeklinde kullanılır. (örneğin math.sqrt(5) math modülünden karekök fonskiyonunu çağırır ve karekök 5 değerini hesaplar!)

Örnek olarak düşey atış probleminde bir ilk hızla yukarı doğru atılan bir cismin herhangi bir yükseklikten hangi zamanlarda geçtğini bulmak için aşağıdaki şekilde bir Python kodu yazmamız gerekir.

In [7]:
# Dikey atis problemi, cismin verilen bir yukseklikten (y)
# gectigi zamanlarin (t1 ve t2) bulunmasi
v0 = 5  # m/s
g = 9.81 # m/s^2
yc = 0.2 # m
import math
t1 = (v0 - math.sqrt(v0**2 - 2*g*yc))/g
t2 = (v0 + math.sqrt(v0**2 - 2*g*yc))/g
print('t={:g} saniye ve {:g} saniyede cismin yuksekligi {:g} metredir'.\
      format(t1, t2, yc))
t=0.0417064 saniye ve 0.977662 saniyede cismin yuksekligi 0.2 metredir

Modül Çağırma

Modül Fonksiyonları Kullanma Yöntemleri

import modul_adi şeklindeki modül çağırma ifadesinin yanı sıra başka modül çağırma şekilleri de vardır.

En sık kullanılan modül çağırma şekillerinden biri

from modul_adi import fonksiyon_adi1,fonksiyon_adi2, ...
In [8]:
from math import sqrt,sin,exp,log,e,sin,pi
print(sqrt(2))
print("sin (90) = {:g}".format(sin(0.5*pi)))
print("ln(e^2) = {:g}".format(log(e**2,e)))
print("e^5 = {:g}".format(exp(5)))
print(log(sin(pi/2.),10))
1.4142135623730951
sin (90) = 1
ln(e^2) = 2
e^5 = 148.413
0.0

Bu şekilde bir modüldeki tüm fonksiyonları from modul_adi import * ifadesiyle de çağrımak mümkündür.

from modül_adi import … ifadesini kullanmanın sakıncası fonksiyonun adını doğrudan kullanmaktan gelir. Python'un büyük bir gücü olan, pek çok programcı / bilim insanı tarafından modüllerin yazılıyor olması, burada bir dezavantaja dönüşür. Zira farklı modüllerde aynı isimle ve farklı işler yapan ya da bir şekilde farklı davranan fonksiyonlar olabilir. Kodun içerisinden her iki modülün birden çağrılması bu durumda problem haline gelebilir. Hatta bazen çağrılan bir modülde yer alan (ve çoğu zaman farkında olunmayan) bir fonksiyonla kullanıcının kendi yazdığı bir fonksiyonun da adı aynı olabilir.

Bu probleme biraz yakından bakalım:

  • import math ifadesi math modülü fonksiyonlarını math isim uzayına (namespace) indirir. Bu nedenle o isim uzayıındaki bir fonksiyonu çağırmak istediğinizde modül adını kullanmanız gerekir: math.sqrt(x) gibi..

  • from math import \* ifadesi math modülündeki tüm fonksiyonları (ya da * yerine fonksiyonun adını yazarsanız o fonksiyonu) sizin kullandığınız isim uzayına (namespace) indirir. Bu uzayı siz kullanıyor olduğunuz için fonksiyon çağırırken sadece adını kullanırsınız: sqrt(x) gibi...

  • Aslında burada “pythonik” bir yol tam olarak tanımlı değildir. Bazen birini bazen diğerini kullanırsınız. from modul import fonksiyon yapısı uzun kodlarda değil de kabukta küçük işlevsel betikler (script) yazarken daha çok tercih edilir.

  • İyi bir çözüm: import numpy as np ya da from math import log as ln gibi ifadelerle lakap (alias) kullanmaktır. Bu durum, fonksiyonların bulunduğu isim uzayına kısa ve kendinizin (ve iç dokümantasyon tutuyorsanız kodunuzu okuyan kişinin) bildiği bir isim verdiğiniz için kullanım kolaylığı getirir.

In [9]:
import math as m
yaricap = 10
cevre = 2*m.pi*yaricap
alan = m.pi*yaricap**2
print("Yaricapi {:d} olan cemberin cevresi {:g}".format(yaricap,cevre))
print("Alani ise {:g} 'dir".format(alan))
Yaricapi 10 olan cemberin cevresi 62.8319
Alani ise 314.159 'dir

Yuvarlama Hataları

Matematiksel olarak sinüs hiperbolik fonksiyonu (sin h)

$$sinh(x) = \frac{(e^{x} - e^{-x})}{2}$$

şeklinde tanımlanır. Şimdi x = 2$\pi$ için sinh(x) 'i farklı şekillerde hesaplayalım.

In [10]:
from math import sinh, exp, e, pi
x = 2*pi
r1 = sinh(x)
r2 = 0.5*(exp(x) - exp(-x))
r3 = 0.5*(e**x - e**(-x))
print("{:.8f} {:.8f} {:.8f}".format(r1,r2,r3))
print("{:.16f} {:.16f} {:.16f}".format(r1,r2,r3))
267.74489404 267.74489404 267.74489404
267.7448940410164369 267.7448940410164369 267.7448940410163232

İşte bu ilginç! Matematiksel olarak eşit olduğunu bildiğimiz üç ifadeden sonuncusu 12. basamaktan sonra diğerlerinden farklılaşan bir sonuç verdi!

Bir başka örneğe bakalım. Aşağıdaki iki ifadenin aynı sonucu vermesi gerek:

$$\frac{1}{49.} * 49. = \frac{1}{51.} * 51 = 1.0000000000000000$$
In [11]:
print(1./49.*49.)
print(1./51.*51.)
0.9999999999999999
1.0

Problem reel sayıların bilgisayarlarca saklanma ve gösterilme şeklinden kaynaklanmaktadır. AST416 Astronomide Sayısal Çözümleme - II dersinde ayrıntılı bir şekilde görebileceiğniz gibi bilgisayarlar sınırlı (genellikle 32) sayıda basamakla reel sayıları saklarlar. Oysa 1/49'u tam olarak ifade etmek için SONSUZ sayıda basamağa ihtiyacınız vardır.

Problemimizde 1/49. tam olarak saklanamıyor ve sonuç 1'den farklı çıkıyor. Aslında durum 1/51. için de aynı ama çıkan sayıyı 51. ile tekrar çarptığımızda yine 1'e ulaşıyoruz. Yani hata final değere kadar yayılmıyor!

round() Fonksiyonu

$round(n, ndigits)$ fonksiyonu Python'un kendi fonksiyonlarından (ing. built-in) bir olup, $n$ ve $ndigits$ adında iki argümanı bulunmaktadır. Bu argümanlardan $n$ yuvarlanması istenen sayıyı, $ndigits$ ise bu sayının kaç basamağı yuvarlanmasının istendiğini round() fonksiyonuna geçirmek için kullanılır. $ndigits$ argümanının varsayılan değeri sıfır olduğundan, sağlanmaması durumunda verilen $n$ sayısı bir tamsayıya yuvarlanır.

In [12]:
print(round(3.1415926535, 2)) # iki basamaga yuvarlama
print(round(3.1415926535)) # tam sayiya yuvarlama
3.14
3

round() fonksiyonunun nasıl çalıştığını anlamak başlangıçta görece kolaydır. Yuvarlanılması istenen ve $ndigits$ opsiyonuyla verilen basamaktan sonraki basamağın değeri 5'ten küçükse basamak değeri olduğu gibi bırakılır, sonraki basamaklar 5'ten büyükse bu kez basamak değeri 1 arttırılır ve gerekiyorsa (9'dan büyük bir basamak değeri olması durumunda) bu daha önceki basamak değerlerine +1 artışla yansıtılır. Ancak sonraki basamak değerlerinin 5 olması durumunda round() fonksiyonu beklenmeyen sonuçlar verebilir.

In [13]:
print(round(1.5)) # 1.5 sayisini tamsayiya yuvarlama
print(round(2.5)) # 2.5 sayisini tamsayiya yuvarlama
2
2

1.5 ve 2.5'in her ikisinin de 2'ye yuvarlanmış olması aslında $round()$ fonksiyonundan değil yine söz konusu sayıların bilgisayarların tüm işlemlerinin dayandığı ikilik sayı sisteminde (ing. binary) temsil edilememesinden kaynaklanmaktadır. Tüm sayı sistemlerinde benzer bir kusur mevcuttur. Örneğin 10 parmaklı canlılar olmamızdan dolayı sıkça kullandığımız onluk sayı sisteminde 1 / 3'ü sonlu sayıda basamak kullanarak temsil edemeyiz (1.333...); oysa ki, 1 / 3 irrasyonel değildir. 1.4 sayısı ikilik sayı sisteminde 1.01100110011 olarak temsil edilirken, sayıyı tamsayıya yuvarlamak istediğmizde noktadan sonraki ilk basamağa bakar (0) ve 1.0'a yuvarlarız. 1.6 sayısı ise ikilik sayı sisteminde 1.10011001100 şeklinde temsil edilir. Bu sayıyı bir tamsayıya yuvarlamak istediğimizde, noktadan sonraki ilk basamağın 1 olduğunu görür ve sayıyı ikilik sayı sisteminde 10'a yuvarlarız (2). Söz konusu sayı 1.5 ise (1.10000....) görüldüğü gibi mükemmel temsil edilemez. Sayının noktadan sonraki ilk basamağı 1 olmakla birlikte ilerleyen basamaklarında yeniden 1 değerleriyle karşılaşılır. Bu nedenle sayı yukarıya yuvarlanır ve ikilik sayı sisteminde 10, onluk sayı sisteminde 2 olur. Oysa ki 2.5 ikilik sayı sisteminde 10.1 şeklinde ifade edilir ve ilerleyen basamaklarında 1 yoktur; bu nedenle aşağı yuvarlanır (10) ve onluk sayı sisteminde 2 değerini alır. Benzer bir temsil sorunu aşağıdaki basit aritmetik ifadenin de beklenilenden farklı çıkmasına yol açmaktadır.

In [14]:
0.1 + 0.1 + 0.1
Out[14]:
0.30000000000000004

math.ceil() ve math.floor() Fonksiyonları

Sorunu teşhis etmek önemli bir şey olmakla birlikte çözümü ayrı bir şeydir. Bunun için öncelikle problemin kodunuzun çözmeye çalıştığı problemi ne düzeyde etkilediğini, yani işlemlerde ihtiyaç duyduğunuz duyarlılığı belirlemeniz gereklidir. Eğer ihtiyaç duyduğunuz duyarlılık matematik kütüphanesinden ($math$) kullanabileceğiniz yukarıya yuvarlayan $ceil()$ ve aşağıya yuvarlayan $floor$ ile çözülebilecek düzeyde ise endişe etmenize gerek yoktur. $math.ceil()$ bir tamsayıya yuvarlanmasını istediğiniz sayıyı ona en yakın ve ondan büyük (ya da eşit) tamsayıya yuvarlar. $math.floor()$ ise tam tersini yapıp, bir tamsayıya yuvarlanmasını istediğiniz sayıyı ona en yakın ve ondan küçüük (ya da eşit) tamsayıya yuvarlar.

In [15]:
import math
print(math.ceil(2.5)) # yukariya yuvarlama
print(math.floor(2.5)) # asagiya yuvarlama
print(math.ceil(-2.5)) # negatif sayi yukariya yuvarlama
print(math.floor(-2.5)) # negatif sayi asagiya yuvarlama
3
2
-2
-3

Konuyu bu noktada daha derinleştirmeden, Python'da soruna en iyi çözümünün $Decimal$ modülü fonksiyonları ile getirilmiş olduğunu belirtip; bu modül ve fonksiyonlarına daha sonra detaylı olarak değineceğimizi vurgulayalım.

Başa Dön

Değişken Türleri ve Dönüşümler

Bir değişken tanımladığımızda Python tarafından adı değişkenin adı, türü değişkene verdiğimiz değerle belirlenen bir nesne oluşturulur.

In [16]:
C = 21
print(C, type(C))
t = 0.2
print(t,type(t))
s = "Python ogreniyorum!"
print(s, type(s))
Cs = '21'
print(Cs, type(Cs))
print(Cs*3)
print(C*3)
print('"21"')
print("AST415 Astronomide Sayisal Cozumleme")
21 <class 'int'>
0.2 <class 'float'>
Python ogreniyorum! <class 'str'>
21 <class 'str'>
212121
63
"21"
AST415 Astronomide Sayisal Cozumleme

Bu değişken türleri arasında dönüşümler yapılabilir. Bu amaçla int(x), (tam sayıya dönüştürme), float(x) (noktalı sayıya dönüştürme), str(x) (metne dönüştürme) gibi fonksiyonlar kullanılır.

In [17]:
D = float(C)
print(D, type(D))
T = int(t)
print(T, type(T)) # sonucta noktadan sonraki basamaklar atildigina dikkat ediniz
t2 = 0.6
print(int(t2)) # int fonksiyonunun yuvarlama islemi degil kesip atma islemi yaptigina dikkat ediniz
t3 = -1.7
print(int(t3)) # durum negatif sayilar icin de aynidir
print(t3) # t3'un kendisi degistirilmedi!
21.0 <class 'float'>
0 <class 'int'>
0
-1
-1.7

Karmaşık Sayılar (Opsiyonel)

Bilindiği gibi karmaşık sayılar i = $\sqrt{-1}$ olmak üzere,

$$a \pm ib$$

şeklinde ifade edilirler.

$u = a + ib$ ve $v = c + id$ olmak üzere,
* $u = v \Rightarrow (a = b)$ ve $(c = d)$
* $-u = -a – ib$
* $u^{*} = a - ib$ (kompleks eşlenik)
* $u + v = (a + c) + (b + d)i$
* $u - v = (a - c) + (b – d)i$
* $uv = (ac − bd) + (bc + ad)i$
* $\frac{u}{v} = \frac{ac + bd}{c^{2} + d^{2}} + \frac{bc − ad}{c^{2} + d^{2}}$
* $|u| = \sqrt{a^{2} + b{2}}$
* $e^{iq}$ = cos q + i sin q$
In [18]:
u = 2.5 + 3j # python'da karmasik sayilar i yerine j ile yazilir
v = 2 # bu bir tam sayi
w = u + v # karmasik sayi + tam sayi
print(w)
a = -2
b = 0.5
s = a + b*1j # karmasik sayi olustur. Dikkat j degil 1j
print(s)
s = complex(a,b) # bu sekilde de karmasik sayi olusturulabilir
print(s)
s*w # iki karmasik sayinin carpimi
s/w # iki karmasik sayinin bolumu
print(s.real) # karmasik sayinin reel kismini verir
print(s.imag) # karmasik sayinin imajiner kismini verir
print(s.conjugate())# karmasik sayinin kompleks eslenigini verir
(4.5+3j)
(-2+0.5j)
(-2+0.5j)
-2.0
0.5
(-2-0.5j)

Python'da karmaşık sayılarla işlemler için cmath modülü fonksiyonları da sıklıkla kullanılır.

In [19]:
from math import sin
r = sin(w) # math modulundeki sinus karmasik sayilarla calismaz
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-19-7fe26ffd38f5> in <module>
      1 from math import sin
----> 2 r = sin(w) # math modulundeki sinus karmasik sayilarla calismaz

TypeError: can't convert complex to float
In [20]:
from cmath import sin,sinh
r1 = sin(8j)
print(r1)
r2 = 1j * sinh(8)
print(r2)
from cmath import exp,cos,sin,pi
q = 8 # rastgele secilmis bir sayi
print(exp(1j*q)) # e^iq
print(cos(q) + 1j*sin(q))
exp(pi*1j) # Euler esitliginin sonucu sizce neden -1 degil?
1490.4788257895502j
1490.4788257895502j
(-0.14550003380861354+0.9893582466233818j)
(-0.14550003380861354+0.9893582466233818j)
Out[20]:
(-1+1.2246467991473532e-16j)

Python'da karmaşık sayılarla reel sayıların birlikte kullanıldığı işlemler için numpy.lib.scimath fonksiyonları da kullanmaktadır.

In [21]:
from math import sqrt
print(sqrt(4)) # 4'un karekoku reel sayidir
print(sqrt(-1)) # hata vermesini bekleriz
2.0
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-dcfdfb93a7cc> in <module>
      1 from math import sqrt
      2 print(sqrt(4)) # 4'un karekoku reel sayidir
----> 3 print(sqrt(-1)) # hata vermesini bekleriz

ValueError: math domain error
In [22]:
from cmath import sqrt
print(sqrt(4)) # 4'un karekoku reel sayi ama karmasik sekilde yazilabilir
print(sqrt(-1)) # sonucun bu kez dogru cikmasini bekliyoruz
from numpy.lib.scimath import sqrt
print(sqrt(4)) # Bu modul sonucu reelse reel,
print(sqrt(-1)) # karmasiksa karmasik sayi olarak verir.
(2+0j)
1j
2.0
1j

Örnek: 2. Dereceden Denklem Çözümü

Aşağıdaki ikinci dereceden denklemi a, b, c'nin farklı değerli için çözmek isitiyor olalım.

$$ax^{2} + bx + c = 0$$
In [23]:
from numpy.lib.scimath import sqrt
a = 1; b = 2; c = 10 # polinom katsayilari
r1 = (-b + sqrt(b**2 - 4*a*c))/(2*a)
r2 = (-b - sqrt(b**2 - 4*a*c))/(2*a)
print("Denklemin cozumu: r1 = {:g}, r2 = {:g}".format(r1,r2))

a = 1; b = 4; c = 1 # polinom katsayilarini degistirelim
r1 = (-b + sqrt(b**2 - 4*a*c))/(2*a)
r2 = (-b - sqrt(b**2 - 4*a*c))/(2*a)
print("Denklemin cozumu: r1 = {:g}, r2 = {:g}".format(r1,r2))
Denklemin cozumu: r1 = -1+3j, r2 = -1-3j
Denklemin cozumu: r1 = -0.267949, r2 = -3.73205

Alıştırmalar

  1. Metre cinsinden verilen bir uzunluğu inch (1 inch = 2.54 cm), foot (1 foot = 12 inch), yard (1 yard = 3 feet) ve mile (1 mil = 1760 yard) birimlerine dönüştüren bir program yazınız. Programınızı 640 metre için test ediniz (25196.85 inch, 2099.74 feet, 699.91 yard ve 0.3977 mil).

  2. Bir cismin yoğunluğu kütlesiyle hacminin çarpımı olarak tanımlanır ($d = m V$). Demir ($d_{Fe} = 7.87 g cm^{-3}$, aliminyum ($d_{Fe} = 2.70 g cm^{-3}$, bakır ($d_{Cu} = 8.96 g cm^{-3}$), hava ($d_{hava} = 1.283 g cm^{-3}$ ve ortalama insan vücudunun ($d_{insan} = 1.01 g cm^{-3}$) bir litresinin kaç kilogram olduğunu bulan bir program yazınız.

  3. Aşağıda verilen programdaki hataları bulup, düzeltiniz ve programı çalıştırınız.

x=1
print('sin(%g)=%g' % (x, sin(x))
  1. Aşağıdaki küçük programcıkları yazıp çalıştırarak verilen ifadelerin doğruluğunu test ediniz. Programların çalışmadığı durumlarda hatalarını bulup, düzelttikten sonra tekrar çalıştırınız.

    a) $sin^{2} + cos^{2} = 1?$

     from math import sin, cos
     x = pi/4
     1_val = math.sin^2(x) + math.cos^2(x)
     print 1_VAL

    b) $s = v_{0} t + 1/2 a t^{2}$ ifadesinde $v_{0} = 3 m/s$ , $t = 1 s$, $a = 2 m/s^{2}$ için s'yi hesaplayınız.

```
v0 = 3 m/s
t = 1 s
a = 2 m/s**2
s = v0.t + 0,5.a.t**2
print s
```    
c) Aşağıdaki ifadeleri doğrulayınız

$$(a + b)^{2} = a^{2} + 2ab + b^{2}$$
$$(a − b)^{2} = a^{2} − 2ab + b^{2}$$


```
a = 3,3 b = 5,3
a2 = a**2
b2 = b**2
eq1_sum = a2 + 2ab + b2
eq2_sum = a2 - 2ab + b2
eq1_pow = (a + b)**2
eq2_pow = (a - b)**2
print('Birinci ifade: %g = %g', % (eq1_sum, eq1_pow))
print('Ikinci ifade: %h = %h', % (eq2_pow, eq2_pow))
```        

  1. Gauss fonksiyonu aşağıdaki ifade ile verilir ve neredeyse her bilim dalında en sık kullanılan fonksiyonlardan biridir. Bu fonksiyonu $\mu = 0$ ortalama değeri, $\sigma = 2$ standart sapması ve $x = 1$ için hesaplayan bir program yazınız.
$$f(x) = \frac{1}{\sqrt{2 \pi} \sigma} exp[- \frac{1}{2}( \frac{x - \mu}{\sigma} )]$$
  1. Aşağıda Santigrat dereceyi Fahrenheit'a dönüştüren bazı kod satırları verilmiştir. Doğru çalışmayacak olan satırları bulup, düzeltiniz ve çalıştırınız.
C = 21; F = 9/5*C + 32; print F
C = 21.0; F = (9/5)*C + 32; print F
C = 21.0; F = 9*C/5 + 32; print(F)
C = 21.0; F = 9.*(C/5.0) + 32;print('.2f' % F)
C = 21.0; F = 9.0*C/5.0 + 32; print("{:.2f}.format(F))
C = 21; F = 9*C/5 + 32; print(F)
C = 21.0; F = (1/5)*9*C + 32; print('%3.2f' % F)
C = 21; F = (1./5)*9*C + 32; print("{:d}.format(F)")
  1. Aşağıda verilen Python ifadelerindeki yanlışları bulunuz ve düzeltiniz.
3b = 2
b2 = b
x = 2
y = X + 4 # 6 eder mi?
from Math import sin
print sin(pi)
pi = "3.1416’
print sin(pi)
c = 2**3**4**3
_ = ((c-180)/c + 32))
indirim_orani = 5%
fiyat = 20.-
etiket_fiyati = 50€
address = www.ozgur.astrotux.org
and = AST415
sinif = 'AST415 Astronomi'de Sayisal Cozumleme-I'
dogrulukdegeri_ = x > 0
cat = kedi = True
Turkce = ['bir dil']
true = cat is kedi
  1. Aşağıdaki formülllere denk Python kodlarının doğru verilip verilmediğini kontrol ediniz. Değillerse düzeltiniz.
$$ax^{2} + bx + c = 0$$$$x_{1, 2} = \frac{-b \pm \sqrt{b^{2} - 4ac}}{2a}$$
a = 2; b = 1; c = 2
from math import sqrt
q = b*b - 4*a*c
q_sr = sqrt(q)
x1 = (-b + q_sr)/2*a
x2 = (-b - q_sr)/2*a
print x1, x2