Not: Asıl proje, math-as-code'dur. Asıl projenin geliştiricilerinden mattdesl, "Belgeyi Python'da yeniden yazmak için zamanım veya isteğim yok ama giriş kısmında diğer dil çatallamalarına bağlantı vermekten mutlu olurum." şeklinde bir açıklama yaptığı için ana depoya değişiklik isteğinin mümkün olmayacağı şimdiki bu yapı tercih edilmiştir.
Bu, geliştiricilere C++ kod karşılıklarını göstererek matematiksel notasyonu anlamalarını kolaylaştırmayı amaçlayan bir referanstır.
Motivasyon: Akademik makaleler, kendi kendini eğiten oyun ve grafik programcıları için korkutucu olabilir :)
Bu kılavuz henüz tamamlanmadı. Hata görürseniz veya katkıda bulunmak isterseniz, lütfen bir kayıt açın veya bir değişiklik isteği gönderin.
Matematiksel semboller; yazara, bağlama ve çalışma alanına (lineer cebir, küme teorisi, vb.) bağlı olarak farklı şeyler ifade edebilir. Bu kılavuz, bir sembolün tüm kullanımlarını kapsamayabilir. Bazı durumlarda, gerçek dünyada referansları (blog yazıları, yayınlar, vb.) verilerek bir sembolün vahşi ortamda nasıl görünebileceğini gösterilmeye çalışılacaktır.
Daha eksiksiz bir liste için Wikipedia'da Matematiksel Sembollerin Listesi'ne bakınız.
Basitlik için, buradaki kod örneklerinin çoğu kayan nokta değerleri üzerinde çalışır ve sayısal olarak sağlam değildir. Bunun neden bir sorun olabileceğine dair daha çok ayrıntı için Mikola Lysenko'nun Sağlam Aritmetik Notlarına bakınız.
Çalışmanın bağlamına ve alanına bağlı olarak çeşitli isimlendirme kuralları vardır ve bunlar her zaman tutarlı değildir. Bununla birlikte, literatürde, aşağıdaki gibi bir deseni takip eden değişken isimleri bulabilirsiniz:
- s - skaler için italik küçük harfler (ör. bir sayı)
- x - vektörler için kalın küçük harfler (ör. bir 2B nokta)
- A - matrisler için büyük harfli harfler (ör. bir 3B dönüşüm)
- θ - sabitler ve özel değişkenler için italik küçük harfli Yunan harfleri (ör. kutup açısı θ, teta)
Bu ayrıca bu kılavuzun da biçimi olacaktır.
Eşittir işareti ='e benzeyen bir dizi sembol vardır. Aşağıda birkaç genel örnek görülebilir:
=
eşitlik için kullanılır (değerler aynıdır)≠
eşitsizlik için kullanılır (değer aynı değildir)≈
yaklaşık olarak eşittir ifadesi için kullanılır (π ≈ 3.14159
):=
tanımlama içindir (A, B olarak tanımlanmıştır)
C++'taysa:
#include <iostream>
#include <cmath>
bool yaklasikEsitMi(double a, double b, double epsilon) {
return fabs(a - b) <= epsilon;
}
int main()
{
// eşitlik
if (2==3)
std::cout << "İki, üçe eşittir.\n";
// eşitsizlik
if (2!=3)
std::cout << "İki, üçe eşit değildir.\n";
// yaklaşık eşitlik
if (yaklasikEsitMi(M_PI, 3.14159265359, 1e-5))
std::cout << M_PI << ", yaklaşık olarak 3.14159265359 değerine eşittir.\n";
return 0;
}
Tanımlama için :=
, =:
ve =
sembollerinin kullanıldığını görebilirsiniz.1
Örneğin aşağıdaki tanımlama, x'i 2kj'nin farklı bir ismi olarak tanımlar:
C++'ta değişkenlerimizi tanımlamak ve takma adlar sağlamak için tür isimlerini kullanabiliriz:
auto x = 2 * k * j;
Ancak, bu değişebilir ve sadece o anki değerlerin bir anlık görüntüsünü alır. Bazı diller, matematiksel bir tanımlamaya daha yakın olan ön işlemci #define ifadesine sahiptir.
C++'ta daha doğru bir tanımlama şöyle olabilir:
const auto x = 2 * k * j;
Öte yandan, aşağıdaki ifade eşitliği temsil eder:
Bu denklem de C++'ta öncekiler gibi yorumlanabilir:
const auto x = 2 * k * j;
Bir karekök işlemi şu şekildedir:
Programlamada, aşağıdaki gibi bir sqrt fonksiyonu kullanırız:
int x = 9;
std::cout << sqrt(x);
// 3
Kompleks sayılar, biçimindeki ifadelerdir, gerçek kısım ve de sanal kısımdır. Sanal sayı şöyle tanımlanır:
C++'ta, karmaşık sayılar için yerleşik fonksiyonlar vardır ve bunlarla karmaşık sayı aritmetiği işlemleri yapılabilir. Örneğin:
#include <iostream>
#include <complex>
int main()
{
//{ re: 3, im: -1 }
std::complex<double> a(3.0, -1.0);
//{ re: 0, im: 1 }
std::complex<double> b(0.0, 1.0);
std::complex<double> c = a * b;
//'1 + 3i'
std::cout << c << '\n';
std::cout << "Gerçek kısım: " << real(c) << '\n';
std::cout << "Sanal kısım: " << imag(c) << '\n';
return 0;
}
C++'ta karmaşık sayılarla ilgili birçok işlem daha kolayca yapılabilir, bunlar hakkında daha çok bilgi için GeeksforGeeks'teki Complex numbers in C++ yazısına bakabilirsiniz.
Nokta ·
ve çarpım ×
sembolleri, içeriğe bağlı olarak farklı kullanımlara sahiptir.
Ne oldukları belli görünebilir, ancak diğer bölümlere devam etmeden önce ince farkları anlamak önemlidir.
Her iki sembol de skalerlerin basit çarpımını temsil edebilir. Aşağıdakiler eşdeğerdir:
Programlama dillerinde çarpma için yıldız işareti kullanma eğilimindeyizdir:
int sonuc = 5 * 4
Çoğunlukla, çarpım işareti sadece belirsizliği önlemek için kullanılır (ör. iki sayı arasında). Şurada tamamen atlayabiliriz örneğin:
Eğer bu değişkenler skalerleri temsil ediyorsa kod şöyle olur:
int sonuc = 3 * k * j
Bir vektörün bir skaler ile çarpımını veya bir vektörün başka bir vektörle öğe öğe çarpımını göstermek için, genellikle nokta ·
veya çarpı ×
sembolleri kullanmayız. Bunlar, lineer cebirde farklı anlamlara sahiptir.
Daha önceki örneğimizi alıp vektörlere uygulayalım. Öğe öğe vektör çarpımını temsil etmek için, Hadamard çarpımında bir açık nokta ∘
kullanıldığını görebilirsiniz.2
Diğer durumlarda yazar, daire içine alınmış bir nokta ⊙
veya dolu bir daire ●
gibi farklı bir gösterimi açıkça tanımlayabilir.3
2B vektörleri temsil etmek için kodda dizilerin [x, y] nasıl kullanılacağını aşağıda görebilirsiniz:
#include <iostream>
#include <array>
std::array<int, 2> carp(std::array<int, 2> a, std::array<int, 2> b)
{
std::array<int, 2> tmp;
for(int i = 0; i < a.size(); i++)
tmp[i] = a[i] * b[i];
return tmp;
}
std::array<int, 2> skalerCarp(std::array<int, 2> a, int b)
{
std::array<int, 2> tmp;
for(int i = 0; i < a.size(); i++)
tmp[i] = a[i] * b;
return tmp;
}
int main()
{
int s = 3;
std::array<int, 2> k = {1, 2};
std::array<int, 2> j = {2, 3};
std::array<int, 2> tmp = carp(k, j);
std::array<int, 2> sonuc = skalerCarp(tmp, s);
for(const auto& s: sonuc)
std::cout << s << ' ';
// [ 6, 18 ]
return 0;
}
Benzer şekilde, matris çarpımı da genellikle nokta ·
veya çarpı sembolü ×
kullanmaz. Matris çarpımı daha sonraki bölümlerde ele alınacaktır.
Nokta sembolü ·
iki vektörün nokta çarpımını göstermek için kullanılabilir. Skaler olarak değerlendirildiğinden bazen bu skaler çarpım olarak adlandırılır.
Lineer cebirin çok yaygın bir özelliğidir ve üç boyutlu bir vektör için aşağıdaki gibi görünebilir:
#include <iostream>
#include <array>
#include <numeric>
int main()
{
std::array<int, 3> k = { 3, -2, 5 };
std::array<int, 3> j = { -1, -4, 2 };
std::cout << std::inner_product(std::begin(k), std::end(k), std::begin(j), 0.0);
//=> 15
return 0;
}
Çarpma sembolü ×
, iki vektörün çapraz çarpımını belirtmek için kullanılabilir.
Bu, kodda şöyle görünecektir:
#include <iostream>
#include <vector>
template <typename T, typename U>
std::vector<T> crossProduct(std::vector<T> const &a, std::vector<U> const &b)
{
std::vector<T> r (a.size());
r[0] = a[1] * b[2] - a[2] * b[1];
r[1] = a[2] * b[0] - a[0] * b[2];
r[2] = a[0] * b[1] - a[1] * b[0];
return r;
}
int main()
{
std::vector<int> k = { 0, 1, 0 };
std::vector<int> j = { 1, 0, 0 };
std::vector<int> sonuc = crossProduct(k, j);
for(const auto& s: sonuc)
std::cout << s << ' ';
//=> [ 0, 0, -1 ]
return 0;
}
Burada, [0, 0, -1] elde ederiz, bu da hem k'nin hem de j'nin dik olduğu anlamına gelir.
Büyük Yunanca Σ
(Sigma), toplama içindir. Başka bir deyişle, bazı sayıları toplar.
Burada i=1
, 1'den başlanıp toplama sembolünün üstündeki sayı olan 100'de sonlanacağı söylenmektedir. Bunlar sırasıyla alt ve üst sınırlardır. Σ
'nın sağındaki i, bize ne topladığımızı gösterir. Kodda:
int toplam = 0;
for (int i = 1; i <= 100; i++) {
toplam += i;
}
std::cout << toplam << '\n';
Toplamın sonucunu tutan toplam
'ın değeri 5050 olur.
İpucu: Tam sayılarla, bu model aşağıdaki şekilde optimize edilebilir:
int n = 100; // üst sınır
int toplam = (n * (n + 1)) / 2;
std::cout << toplam << '\n';
İşte, i'nin veya "toplanacak şeyin" farklı olduğu başka bir örnek:
Kodda:
int toplam = 0;
for (int i = 1; i <= 100; i++) {
toplam += (2 * i + 1);
}
std::cout << toplam << '\n';
Toplamın sonucu 10200'dür.
Notasyon, bir for döngüsünün iç içe olduğuna benzer şekilde iç içe olabilir. Yazar, sırayı değiştirmek için parantez içine almadığı sürece, en sağdaki toplama sembolünü ilk önce değerlendirmelisiniz. Ancak, aşağıdaki durumda, sınırlı miktarlarla uğraştığımız için, sıranın önemi yoktur:
Kodda:
int toplam = 0;
for (int i = 1; i <= 2; i++) {
for (int j = 4; j <= 6; j++) {
toplam += (3 * i * j);
}
}
std::cout << toplam << '\n';
veya
int toplam = 0;
for (int j = 4; j <= 6; j++) {
for (int i = 1; i <= 2; i++) {
toplam += (3 * i * j);
}
}
std::cout << toplam << '\n';
Burada her iki kod parçası için de toplam
135 olacaktır.
Büyük Pi, toplama sembolüne çok benzer, tek farkı bir dizi değerin toplamını değil çarpımını bulmak için kullanılır.
Şuna bakalım:
Bu, kodda şöyle görünebilir:
int sonuc = 1;
for (int i = 1; i <= 6; i++) {
sonuc *= i;
}
std::cout << sonuc << '\n';
sonuc
, 720 olarak hesaplanacaktır.
Çubuk olarak da bilinen boru sembolleri, içeriğe bağlı olarak farklı şeyler ifade edebilir. Aşağıda üç yaygın kullanım bulunmaktadır: mutlak değer, Öklid normu ve determinant.
Bu üç özelliğin tümü, bir nesnenin uzunluğunu tanımlar.
x sayısı için |x|
, x'in mutlak değerini belirtir. Kodda:
#include <iostream>
#include <cmath>
int main()
{
int x = -5;
int sonuc = abs(x);
std::cout << sonuc << '\n';
// => 5
return 0;
}
v vektörü için ‖v‖
, v'nin Öklid normudır. Bu, ayrıca bir vektörün "büyüklüğü" veya "uzunluğu" olarak da adlandırılır.
Çoğunlukla mutlak değer gösterimiyle karıştırılmasını önlemek için çift çubukla gösterilir, ancak bazen tek çubukla da gösteriliyor olabilir:
İşte bir 3B vektörü temsil etmek için [x, y, z] şeklindeki bir dizinin kullanılışına örnek:
#include <iostream>
#include <cmath>
#include <vector>
int uzunluk (std::vector<int> vektor) {
int x = vektor[0];
int y = vektor[1];
int z = vektor[2];
return sqrt(x * x + y * y + z * z);
}
int main()
{
std::vector<int> v = { 0, 4, -3 };
std::cout << uzunluk(v) << '\n';
//=> 5
return 0;
}
Bu işlemi yapmak için C++'ta birden çok yol vardır, bazılarını This Thread'deki Euclidean norm yazısında görebilirsiniz, buradan alınan bir gerçekleştirim aşağıdadır:
#include <iostream>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>
int main()
{
boost::numeric::ublas::vector<int> uv(3);
uv[0] = 0;
uv[1] = 4;
uv[2] = -3;
std::cout << "Öklid normu: " << boost::numeric::ublas::norm_2(uv) << std::endl;
return 0;
}
Bir A matrisi için |A|
, A matrisinin determinantı demektir.
Aşağıda, bir 2x2 matrisin determinantını hesaplayan örnek verilmiştir:
#include <iostream>
#include <armadillo>
int main()
{
arma::mat A = "1 0; 0 1;";
std::cout << arma::det(A) << '\n';
return 0;
}
Yukarıdaki gerçekleştirimde lineer cebir ve bilimsel bilgi işlem kütüphanesi olan Armadillo kullanılmıştır, derleyebilmeniz için başlık dosyasını programınıza dahil etmeli ve örneğin CMake kullanıyorsanız kütüphaneyi target_link_libraries(${PROJECT_NAME} -larmadillo)
ile programınıza bağlamalısınız.
Eğer bir kütüphane kullanmak yerine determinant alan fonksiyonu kendiniz yazmak isterseniz GeeksforGeeks'teki Determinant of a Matrix, Stack Overflow'daki Fastest way to calculate determinant? ve Code Review'daki Determinant of a matrix yazılarına bakabilirsiniz. Bunlardan sonuncusundan alınmış gerçekleştirim aşağıdadır:
#include <iostream>
#include <vector>
static int CalcDeterminant(std::vector<std::vector<int>> Matrix)
{
// Bu fonksiyon matrisin determinantını hesaplar
// herhangi bir boyuttaki matrisin determinantını özyinelemeli olarak hesaplayabilir
int det = 0; // determinant değeri burada saklanır
if (Matrix.size() == 1)
{
return Matrix[0][0]; // hesaplama gerekmez
}
else if (Matrix.size() == 2)
{
// Bu durumda, 2 boyutlu matrisin determinantını bir öntanımlı prosedürde hesaplıyoruz
det = (Matrix[0][0] * Matrix[1][1] - Matrix[0][1] * Matrix[1][0]);
return det;
}
else
{
// Bu durumda, 2'den büyük, örneğin 3x3 boyutlarına sahip bir kare matrisin
// determinantını hesaplıyoruz
for (int p = 0; p < Matrix[0].size(); p++)
{
//this loop iterate on each elements of the first row in the matrix.
//at each element we cancel the row and column it exist in
//and form a matrix from the rest of the elements in the matrix
std::vector<std::vector<int>> TempMatrix; // to hold the shaped matrix;
for (int i = 1; i < Matrix.size(); i++)
{
// iteration will start from row one cancelling the first row values
std::vector<int> TempRow;
for (int j = 0; j < Matrix[i].size(); j++)
{
// iteration will pass all cells of the i row excluding the j
//value that match p column
if (j != p)
{
TempRow.push_back(Matrix[i][j]);//add current cell to TempRow
}
}
if (TempRow.size() > 0)
TempMatrix.push_back(TempRow);
//after adding each row of the new matrix to the vector tempx
//we add it to the vector temp which is the vector where the new
//matrix will be formed
}
det = det + Matrix[0][p] * pow(-1, p) * CalcDeterminant(TempMatrix);
//then we calculate the value of determinant by using a recursive way
//where we re-call the function by passing to it the new formed matrix
//we keep doing this until we get our determinant
}
return det;
}
};
int main()
{
std::cout << CalcDeterminant({{1, 0}, {0, 1}}) << '\n';
return 0;
}
Geometride, bir karakterin üstündeki "şapka" sembolü, bir birim vektörünü temsil etmek için kullanılır. Örneğin, aşağıdaki a'nın birim vektörüdür:
Kartezyen uzayda, bir birim vektörü tipik olarak 1 uzunluğundadır. Bu, vektörün her bir parçasının -1.0 ila 1.0 aralığında olacağı anlamına gelir. Aşağıda bir 3 boyutlu vektörü bir birim vektörüne normalize ediyoruz:
#include <iostream>
#include <cmath>
#include <vector>
std::vector<double> normalize(std::vector<double> vec) {
double x = vec[0];
double y = vec[1];
double z = vec[2];
double squaredLength = x * x + y * y + z * z;
if (squaredLength > 0) {
double length = sqrt(squaredLength);
vec[0] = x / length;
vec[1] = y / length;
vec[2] = z / length;
}
return vec;
}
int main()
{
std::vector<double> a = { 0.0, 4.0, -3.0 };
for(const auto& s: normalize(a))
std::cout << s << ' ';
//=> [ 0, 0.8, -0.6 ]
return 0;
}
Benzer bir gerçekleştirim için 2D and 3D vector normalization and angle calculation in C++ yazısına da bakabilirsiniz.
Küme teorisinde, bir şeyin bir kümenin elemanı olup olmadığını tanımlamak için "elemanıdır" sembolü ∈
ve ∋
kullanılabilir. Örneğin:
Burada {3, 9, 14}
değerlerinden oluşan bir A sayı kümesi var ve diyoruz ki 3
, bu kümenin bir "elemanıdır".
C++'ta basit bir uygulama şöyle olabilir:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
int n1 = 8;
int n2 = 9;
std::vector<int> vektor = { 3, 9, 14 };
auto result1 = std::find(std::begin(vektor), std::end(vektor), n1);
auto result2 = std::find(std::begin(vektor), std::end(vektor), n2);
if (result1 != std::end(vektor)) {
std::cout << "vektor'de var: " << n1 << '\n';
} else {
std::cout << "vektor'de yok: " << n1 << '\n';
}
if (result2 != std::end(vektor)) {
std::cout << "vektor'de var:: " << n2 << '\n';
} else {
std::cout << "vektor'de yok: " << n2 << '\n';
}
return 0;
}
Bununla birlikte, sadece benzersiz değerleri tutan bir std::set
kullanmak daha doğru olacaktır:
#include <iostream>
#include <algorithm>
#include <set>
int main()
{
int n1 = 8;
int n2 = 9;
std::set<int> sayiKumesi = { 3, 9, 14 };
// if(sayiKumesi.contains(n1)) { // C++20'de gelecek
if(sayiKumesi.count(n1) > 0) {
std::cout << n1 << " var\n";
} else {
std::cout << n1 << " yok\n";
}
// if(sayiKumesi.contains(n2)) { // C++20'de gelecek
if(sayiKumesi.count(n2) > 0) {
std::cout << n2 << " var\n";
} else {
std::cout << n2 << " yok\n";
}
return 0;
}
Geriye doğru olan ∋
de aynıdır, ancak sıra değişir:
Ayrıca ∉
ve ∌
gibi "bir elemanı değildir" sembollerini de kullanabilirsiniz:
Denklemler arasında bazı büyük yazı tahtası kalını yazıyüzlü harfler görebilirsiniz. Genellikle, bu kümeleri tanımlamak için kullanılır.
Örneğin, k'yi, ℝ
kümesinin bir elemanı olarak tanımlayabiliriz.
Aşağıda birkaç yaygın küme ve onların sembolleri listelenmiştir.
Büyük ℝ
, gerçel sayı kümesini tanımlar. Bu sayı kümesi tamsayıları, rasyonel sayıları ve irrasyonel sayıları içerir.
C++, kayan nokta sayıları ve tamsayıları aynı tür olarak ele alır, dolayısıyla aşağıdakiler bizim k ∈ ℝ örneğimizin basit bir testi olacaktır:
#include <iostream>
#include <cmath>
bool isReal (double k) {
return (!std::isnan(k) && std::isfinite(k));
}
int main()
{
double n1 = sqrt(-2);
if (isReal(n1))
std::cout << n1 << " gercel sayi" << '\n';
else
std::cout << n1 << " gercel sayi degil" << '\n';
return 0;
}
Rasyonel sayılar, bir kesir veya oran (⅗
gibi) olarak ifade edilebilen gerçel sayılardır. Rasyonel sayılar payda olarak sıfır alamaz.
Bu aynı zamanda tüm tamsayıların rasyonel sayılar olduğu anlamına gelir, çünkü payda 1 olarak ifade edilebilir.
Öte yandan irrasyonel bir sayı, π (PI) gibi, bir oran olarak ifade edilemeyen bir sayıdır.
Tam sayı, kesirli kısmı olmayan gerçel bir sayıdır. Bunlar pozitif veya negatif olabilir.
C++'ta basit bir test şöyle görünebilir:
#include <iostream>
#include <cmath>
bool isInteger (double n) {
return std::floor(n) == n;
}
int main()
{
double n1 = 1.0;
if (isInteger(n1))
std::cout << n1 << " tam sayı" << '\n';
else
std::cout << n1 << " tam sayı degil" << '\n';
return 0;
}
Doğal bir sayı, pozitif olan ve negatif olmayan bir tam sayıdır. Bağlama ve çalışm alanına bağlı olarak, küme sıfırı içerebilir veya içermeyebilir, bu nedenle şunlardan birine benzeyebilir:
{ 0, 1, 2, 3, ... }
{ 1, 2, 3, 4, ... }
İlki bilgisayar bilimlerinde daha yaygındır, örneğin:
#include <iostream>
#include <cmath>
bool isNaturalNumber (double n) {
return (std::floor(n) == n && n >= 0);
}
int main()
{
double n1 = 1.0;
if (isNaturalNumber(n1))
std::cout << n1 << " doğal sayı" << '\n';
else
std::cout << n1 << " doğal sayı degil" << '\n';
return 0;
}
Karmaşık bir sayı, 2B düzlemde bir koordinat olarak görülen gerçek ve sanal bir sayının birleşimidir. Daha çok bilgi için A Visual, Intuitive Guide to Imaginary Numbers (Sanal Sayılar için Görsel, Sezgisel Bir Kılavuz)'a bakabilirsiniz.
Fonksiyonlar matematiğin temel özellikleridir ve fonksiyon kavramını koda çevirmek epey kolaydır.
Bir fonksiyon, bir giriş bir çıkış değeriyle ilişkilendirir. Örneğin, aşağıdaki bir fonksiyondur:
Bu fonksiyona bir isim verebiliriz. Yaygın olarak, bir fonksiyonu tanımlamak için ƒ
kullanırız, fakat A(x)
veya başka bir şey de olabilir.
Kodda kare
olarak isimlendirip şöyle yazabiliriz:
#include <iostream>
#include <cmath>
double kare (double x) {
return std::pow(x, 2);
}
int main()
{
double sayi = 4.0;
std::cout << sayi << " sayısının karesi " << kare(sayi) << " olur.\n";
return 0;
}
Bazen bir fonksiyon adlandırılmaz ve bunun yerine çıktı yazılır.
Yukarıdaki örnekte, x giriş, kare alma ilişki ve y çıkıştır.
Fonksiyonlar, bir programlama dilinde olduğu gibi birden çok parametreye de sahip olabilir. Bunlar matematikte argümanlar olarak bilinir ve bir fonksiyonun aldığı argümanların sayısı fonksiyonun ilişki derecesi (iki nesne arasındaki ilişki sayısı) olarak bilinir.
Kodda:
double uzunluk (double x, double y) {
return std::sqrt(x * x + y * y);
}
Bazı fonksiyonlar x giriş değerine bağlı olarak farklı ilişkiler kullanacaktır.
Aşağıdaki ƒ fonksiyonu giriş değerine bağlı olarak iki "alt fonksiyon" arasında seçim yapar.
Bu, koddaki if
/ else
yapısına çok benzer. Sağ taraftaki koşullar genellikle "for x < 0" veya "if x = 0" olarak yazılır. Durum doğruysa, soldaki işlev kullanılır.
Parçalı fonksiyonlarda, "aksi halde" ve "başka durumlarda" ifadeleri, koddaki else
ifadesine benzer.
double f (double x) {
if (x >= 1) {
return ((std::pow(x, 2) - x) / x);
} else {
return 0;
}
}
Matematikte her yerde bulunan bazı fonksiyon isimleri vardır. Bir programcı için, bunlar dilde bulunan "yerleşik" fonksiyonlara benzer olabilir.
Böyle bir örnek sgn fonksiyonudur. Bu, işaret fonksiyonu veya diğer adıyla signum fonksiyonudur. Bunu tanımlamak için parçalı fonksiyon notasyonunu kullanalım:
Kodda şöyle görünebilir:
short sgn (double x) {
if (x < 0) return -1;
if (x > 0) return 1;
return 0;
}
veya:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
Armadillo, Boost gibi çeşitli kütüphaneler aracılığıyla da işaret fonksiyonunu kullanabilirsiniz.
Literatürde, bazen fonksiyonlar daha açık notasyon ile tanımlanabilir. Örneğin, daha önce bahsettiğimiz kare
fonksiyona geri dönelim:
Bu, aşağıdaki biçimde de yazılabilir:
Burada bir kuyruğu olan ok, x, x2 ile eşleşir örneğinde olduğu gibi "eşleşir" anlamına gelir.
Bazen, belli olmadığı zaman, notasyon aynı zamanda fonksiyonun tanım kümesini ve değer kümesini de tanımlayacaktır. ƒ fonksiyonunun daha resmi bir tanımı şu şekilde yazılabilir:
Bir fonksiyonun tanım kümesini ve değer kümesini, sırasıyla giriş ve çıkış türleri gibidir. İşte, bir tamsayı çıkaran ve önceki sgn fonksiyonumuzu kullanan başka bir örnek:
Buradaki (kuyruğu olmayan) ok bir kümeyi bir başka kümeye eşlemek için kullanılır.
JavaScript'te ve dinamik olarak yazılan diğer dillerde, bir fonksiyonun giriş/çıkışını açıklamak ve doğrulamak için belgelendirme ve/veya çalışma zamanı kontrolleri kullanabilirsiniz. Flowtype gibi bazı araçlar, JavaScript'e statik yazımı getirmeye çalışır. Java ve C++ gibi diğer diller, bir fonksiyonun giriş/çıkışının statik türlerine bağlı olarak gerçek fonksiyon aşırı yüklemesine izin verir. Bu, matematiğe daha yakındır: farklı bir tanım kümesi kullanıyorlarsa, iki fonksiyon aynı değildir.
Birincil işareti (′
) genellikle, değişken isimlerinde, farklı bir isim vermeden benzer olan şeyleri tanımlamak için kullanılır. Bazı dönüşümlerden sonra "sonraki değeri" tanımlayabilir.
Örneğin, bir 2B noktayı (x, y) alıp döndürürsek sonucu (x′, y′) olarak adlandırabilirsiniz. Veya M matrisinin transpozesi M′ olarak adlandırılabilir.
Kodda, genellikle değişkene, donusturulmusPozisyon
gibi daha açıklayıcı bir ad atarız.
Bir matematiksel fonksiyon için, birincil işareti genellikle bu fonksiyonun türevini tanımlar. Türevler ilerideki bir bölümde açıklanacaktır. Daha önceki bir fonksiyonumuzu ele alalım:
Bunun türevi bir birincil ′
işaretiyle yazılabilir:
Kodda:
template <typename T> T f (T x) {
return pow(x, 2);
}
template <typename T> T fPrime (T x) {
return (2 * x);
}
İkinci türevi ƒ′′ ve üçüncü türevi ƒ′′′ tanımlamak için birden çok birincil işareti kullanılabilir. Bundan sonrakiler için yazarlar genellikle Roma rakamlarını ƒIV veya üst simge sayılarını ƒ(n) kullanır.
Özel parantezler ⌊x⌋
ve ⌈x⌉
, sırasıyla taban ve tavan fonksiyonlarını temsil eder.
Kodda:
std::cout << std::ceil(2.4) << '\n'; // 3
std::cout << std::floor(2.4) << '\n'; // 2
İki sembol ⌊x⌉
şeklinde birleştirildiğinde, tipik olarak en yakın tam sayıya yuvarlayan bir fonksiyonu temsil eder:
Kodda:
std::cout << std::round(2.4) << '\n'; // 2
Oklar genellikle fonksiyon notasyonunda kullanılır. Aşağıda görebileceğiniz birkaç farklı alan listelenmiştir.
Mantıkta bazen ⇒
ve →
gibi oklar, maddi gerektirme için kullanılır. Yani, A doğruysa, B de doğrudur.
Bunun kod olarak yorumu şöyle olabilir:
bool A = true;
bool B;
if (A == true) {
B = true;
}
std::cout << "B: " << B << '\n'; // 1
Oklar iki yöne doğru da olabilir ⇐
⇒
veya iki yönlü de olabilir ⇔
. A ⇒ B ve B ⇒ A olduğunda aşağıdakine eşdeğer oldukları söylenir:
Matematikte, <
>
≤
ve ≥
, genellikle bunları kodda kullandığımız şekilde kullanılır, sırasıyla: daha küçük, daha büyük, daha küçük veya eşit veya daha büyük veya eşit.
50 > 2 == true;
2 < 10 == true;
5 <= 4 == false;
4 >= 4 == true;
Pek sık olmasa da, bu semboller üzerinde olumsuzluk anlamı katan bir eğik çizgi görebilirsiniz. Örneğin k, "büyük değildir" j'den demek için:
≪
ve ≫
bazen kaydadeğer bir eşitsizliği temsil etmek için kullanılır. Yani, k'nın, j'den büyüklük kertesi (basamaksal büyüklük) olarak daha büyük olduğunu ifade eder.
Matematikte, basamaksal büyüklük epey spesifiktir; sadece "büyük bir fark" anlamı taşımaz. Yukarıdakilerin basit bir örneği:
#include <iostream>
#include <cmath>
double orderOfMagnitude (double n) {
return std::abs(std::floor(std::log10(std::abs(n))));
}
int main()
{
double a = 3e-4;
double b = 2e3;
if (orderOfMagnitude(a) > orderOfMagnitude(b))
std::cout << "a'nın basamaksal büyüklüğü (" << orderOfMagnitude(a)
<< "), b'ninkinden (" << orderOfMagnitude(b) << ") büyük.\n";
else if (orderOfMagnitude(a) < orderOfMagnitude(b))
std::cout << "a'nın basamaksal büyüklüğü (" << orderOfMagnitude(a)
<< "), b'ninkinden (" << orderOfMagnitude(b) << ") küçük.\n";
else
std::cout << "a'nın basamaksal büyüklüğü b'ninkine eşit: "
<< orderOfMagnitude(a) << '\n';
return 0;
}
Okların mantıktaki başka bir kullanımı, birleşim ∧
ve ayrışımdır ∨
. Bunlar sırasıyla programlamadaki AND
ve OR
operatörlerine benzemektedir.
Aşağıdaki birleşimi ∧
, mantıksal AND
'i gösterir:
C++’ta &&’i kullanırız. Aşağıdaki örnekte k'nin doğal bir sayı olduğu varsayıldığında, mantık k'nin 3 olup olmadığını kontrol eder:
int k = 3;
if (k > 2 && k < 4) {
std::cout << "k'nın değeri 3'tür: " << k << '\n';
}
Aşağı ok ∨
, OR operatörü gibi mantıksal bir ayrışımdır.
Kodda:
A || B
Bazen ¬
, ~
ve !
sembolleri, mantıksal NOT
'ı temsil etmek için kullanılır. Örneğin, ¬A, sadece A yanlışsa doğrudur.
not sembolünün kullanımına basit bir örnek:
Bunu kodda nasıl yorumlayabileceğimizin bir örneği de:
int x = 1;
int y = 2;
if (x != y) {
std::cout << "x, y'ye eşit değildir.\n";
}
Not: Tilde ~
, içeriğe bağlı olarak birçok farklı anlama sahiptir. Örneğin, satır eşdeğerliği (matris teorisinde) veya aynı basamaksal büyüklük derecesi (eşitlik bölümünde ele alınmıştır) anlamlarına gelebilir.
Bazen bir fonksiyon, bir dizi değerle sınırlı gerçel sayılarla ilgilenir, böyle bir kısıtlama, bir aralık kullanılarak temsil edilebilir.
Örneğin sıfır ve bir arasındaki sayıları, sıfır veya biri dahil ederek veya etmeyerek aşağıdaki gibi temsil edebiliriz:
-
Sıfır ve bir dahil değil: $ (0, 1) $
-
Sıfır dahil ama bir dahil değil: $ [0, 1) $
-
Sıfır dahil değil ama bir dahil: $ (0, 1] $
-
Sıfır ve bir dahil: $ [0, 1] $
Örneğin, birim küp içinde bir x
noktasını gösterdiğimizi belirtmek istersek:
C++'ta aralıklar üzerinde çalışmak için Boost.Icl, libieeep1788 ve Moore kullanılabilir.
Kodda iki elemanlı 1b dizi kullanarak bir aralığı şu şekilde temsil edebiliriz:
TODO: cpp'ye çevrilecek
var nextafter = require('nextafter')
var a = [nextafter(0, Infinity), nextafter(1, -Infinity)] // open interval
var b = [nextafter(0, Infinity), 1] // interval closed on the left
var c = [0, nextafter(1, -Infinity)] // interval closed on the right
var d = [0, 1] // closed interval
Aralıklar küme işlemleriyle birlikte kullanılır:
-
kesişim, örneğin: $ [3, 5) \cap [4, 6] = [4, 5) $
-
birleşim, örneğin: $ [3, 5) \cup [4, 6] = [3, 6] $
-
fark, örneğin: $ [3, 5) - [4, 6] = [3, 4) $ ve $ [4, 6] - [3, 5) = [5, 6] $
Kodda:
TODO: cpp'ye çevrilecek
var Interval = require('interval-arithmetic')
var nextafter = require('nextafter')
var a = Interval(3, nextafter(5, -Infinity))
var b = Interval(4, 6)
Interval.intersection(a, b)
// {lo: 4, hi: 4.999999999999999}
Interval.union(a, b)
// {lo: 3, hi: 6}
Interval.difference(a, b)
// {lo: 3, hi: 3.9999999999999996}
Interval.difference(b, a)
// {lo: 5, hi: 6}
Bu rehberi sevdiniz mi? Daha iyi hale getirmek için değişiklik isteğinde veya özellik isteğinde bulunmaya ne dersiniz!
Nasıl katkıda bulunacağınıza ilişkin ayrıntılar için, bkz: CONTRIBUTING.md.
MIT, detaylar için bkz: LICENSE.md.