>>> (float('inf')+0j)*1
(inf+nanj)
Neden? Bu, kodumda kötü bir hataya neden oldu.
1
Çarpımsal kimlik neden vermiyor (inf + 0j)
?
>>> (float('inf')+0j)*1
(inf+nanj)
Neden? Bu, kodumda kötü bir hataya neden oldu.
1
Çarpımsal kimlik neden vermiyor (inf + 0j)
?
Yanıtlar:
1
İlk olarak bir karmaşık sayı dönüştürülür 1 + 0j
, burada bir daha sonra açar inf * 0
bir sonuçlanan çarpma nan
.
(inf + 0j) * 1
(inf + 0j) * (1 + 0j)
inf * 1 + inf * 0j + 0j * 1 + 0j * 0j
# ^ this is where it comes from
inf + nan j + 0j - 0
inf + nan j
1
atılır 1 + 0j
.
array([inf+0j])*1
ayrıca array([inf+nanj])
. Gerçek çarpmanın C / C ++ kodunda bir yerde gerçekleştiğini varsayarsak, bu, _Complex veya std :: complex kullanmak yerine CPython davranışını taklit etmek için özel kod yazdıkları anlamına mı gelir?
numpy
bir merkezi sınıfa sahiptir ufunc
. ufunc
dizilerle çalışmayı çok kolay hale getiren tüm bu zorlu yönetici adımlarını yayınlama yönetimiyle ilgilenir. Daha kesin bir ifadeyle, belirli bir operatör ile genel makine arasındaki emeğin bölünmesi, belirli operatörün, ele almak istediği her girdi ve çıktı elemanı türü kombinasyonu için bir "en iç döngü" kümesi uygulamasıdır. Genel makine, herhangi bir dış döngü ile ilgilenir ve en içteki en iyi eşleşmeyi seçer ...
types
öznitelik aracılığıyla sağlanan iç döngülerin listesine erişebiliriz, özellikle karma türlerin neredeyse hiç olmadığını görebiliriz, özellikle de float ile karmaşık arasında karışmayan hiçbiri . np.multiply
['??->?', 'bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L', 'qq->q', 'QQ->Q', 'ee->e', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D', 'GG->G', 'mq->m', 'qm->m', 'md->m', 'dm->m', 'OO->O']
"efdg"
"FDG"
Mekanik olarak kabul edilen yanıt elbette doğrudur, ancak daha derin bir yanıt verilebileceğini iddia ediyorum.
İlk olarak, @PeterCordes'in bir yorumda yaptığı gibi soruyu açıklığa kavuşturmakta fayda var: "karmaşık sayılar için inf + 0j üzerinde çalışan bir çarpımsal kimlik var mı?" veya başka bir deyişle, OP'nin karmaşık çarpmanın bilgisayar uygulamasında bir zayıflık gördüğü şeydir veyainf+0j
Kutupsal koordinatları kullanarak, karmaşık çarpımı bir ölçekleme ve bir dönüş olarak görebiliriz. Sonsuz bir "kolu", bir ile çarpma durumunda olduğu gibi 0 derece bile döndürmek, ucunu sonlu bir hassasiyetle yerleştirmeyi bekleyemeyiz. Öyleyse aslında, temelde doğru olmayan bir şey var inf+0j
, yani sonsuza ulaşır ulaşmaz sonlu bir ofset anlamsız hale geliyor.
Arka plan: Bu sorunun etrafında döndüğü "büyük şey", bir sayı sistemini genişletme meselesidir (gerçekleri veya karmaşık sayıları düşünün). Bunu yapmak istemenin bir nedeni, bir sonsuzluk kavramı eklemek ya da matematikçi olursa "sıkıştırmak" olabilir. Başka nedenler de var ( https://en.wikipedia.org/wiki/Galois_theory , https://en.wikipedia.org/wiki/Non-standard_analysis ), ancak buradakilerle ilgilenmiyoruz.
Böyle bir uzantı ile ilgili zor olan şey, elbette, bu yeni sayıların mevcut aritmetiğe uymasını istememizdir. En basit yol sonsuza tek bir eleman eklemek ( https://en.wikipedia.org/wiki/Alexandroff_extension ) ve sıfıra bölünmüş sıfır dışında herhangi bir şeye eşit yapmaktır. Bu, gerçekler ( https://en.wikipedia.org/wiki/Projectively_extended_real_line ) ve karmaşık sayılar ( https://en.wikipedia.org/wiki/Riemann_sphere ) için çalışır.
Tek noktalı sıkıştırmanın basit ve matematiksel olarak sağlam olmasına rağmen, birden fazla infinti içeren "daha zengin" uzantılar aranmıştır. Gerçek kayan noktalı sayılar için IEEE 754 standardında + inf ve -inf vardır ( https://en.wikipedia.org/wiki/Extended_real_number_line ). Doğal ve anlaşılır görünüyor ama zaten bizi çemberlerden atlamaya ve https://en.wikipedia.org/wiki/Signed_zero gibi şeyler icat etmeye zorluyor-0
Karmaşık düzlemin birden fazla inf uzantısına ne dersiniz?
Bilgisayarlarda, karmaşık sayılar tipik olarak biri gerçek diğeri hayali kısım için iki fp gerçeğini birbirine yapıştırarak gerçekleştirilir. Her şey sonlu olduğu sürece bu tamamen iyidir. Bununla birlikte, sonsuzluklar düşünüldüğünde işler yanıltıcı hale gelir.
Karmaşık düzlem, tüm düzlemi e ^ phij ile çarpmak, etrafındaki bir phi radyan dönüşüyle aynı olduğundan, karmaşık aritmetik ile güzel bir şekilde bağlanan doğal bir dönme simetrisine sahiptir 0
.
Şimdi, işleri basit tutmak için karmaşık fp, temeldeki gerçek sayı uygulamasının uzantılarını (+/- inf, nan vb.) Kullanır. Bu seçim o kadar doğal görünebilir ki bir seçim olarak algılanmıyor bile, ama ne anlama geldiğine daha yakından bakalım. Karmaşık düzlemin bu uzantısının basit bir görselleştirmesi şöyle görünür (I = sonsuz, f = sonlu, 0 = 0)
I IIIIIIIII I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I ffff0ffff I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I IIIIIIIII I
Ancak gerçek bir karmaşık düzlem, karmaşık çarpmaya saygı duyan bir düzlem olduğundan, daha bilgilendirici bir izdüşüm olacaktır.
III
I I
fffff
fffffff
fffffffff
I fffffffff I
I ffff0ffff I
I fffffffff I
fffffffff
fffffff
fffff
I I
III
Bu projeksiyonda, sadece çirkin değil, aynı zamanda OP'nin maruz kaldığı türden sorunların kökü olan sonsuzlukların "eşitsiz dağılımını" görüyoruz: Çoğu sonsuzluk (+/- sonsuz, sonlu) ve (sonlu, + / -inf) dört ana yönde bir araya toplanır, diğer tüm yönler yalnızca dört sonsuzluk ile gösterilir (+/- inf, + -inf). Karmaşık çarpımı bu geometriye genişletmenin bir kabus olması şaşırtıcı olmamalı .
C99 şartnamesinin Ek G'si, nasıl inf
ve nan
etkileşimde bulunmaya (esasen inf
kozlar nan
) ilişkin kuralları esnetmek de dahil olmak üzere, çalışması için elinden gelenin en iyisini yapmaya çalışır . OP'nin problemi, gerçekleri ve önerilen tamamen hayali bir türü karmaşık hale getirmemekle bir kenara atılır, ancak gerçek 1'in karmaşık 1'den farklı davranması bana bir çözüm olarak gelmez. Anlaşılır bir şekilde, Ek G, iki sonsuzluğun ürününün ne olması gerektiğini tam olarak belirlemekte yetersiz kalıyor.
Daha iyi bir sonsuzluk geometrisi seçerek bu sorunları denemek ve düzeltmek cazip geliyor. Genişletilmiş gerçek çizgiye benzer şekilde, her yön için bir sonsuzluk ekleyebiliriz. Bu yapı, projektif düzleme benzer, ancak zıt yönleri bir araya getirmez. Sonsuzluklar kutupsal koordinatlarda gösterilecektir, inf xe ^ {2 omega pi i}, ürünleri tanımlamak basit olacaktır. Özellikle, OP'nin sorunu oldukça doğal bir şekilde çözülecektir.
Ama burası iyi haberin bittiği yerdir. Yeni stil sonsuzluklarımızın gerçek veya hayali parçalarını çıkaran fonksiyonları desteklemesini gerektirerek - mantıksız bir şekilde değil - bir şekilde kareye geri fırlatılabiliriz. Ekleme başka bir sorundur; iki antipodal olmayan sonsuzluk ekleyerek açıyı tanımsız olarak ayarlamamız gerekirdi, yani nan
açının iki giriş açısı arasında olması gerektiği tartışılabilir, ancak bu "kısmi nan -liği" temsil etmenin basit bir yolu yoktur)
Tüm bunların ışığında, belki de eski tek noktalı kompaktlaştırma yapılacak en güvenli şeydir. Belki Ek G'nin yazarları, cproj
tüm sonsuzlukları bir araya toplayan bir işlevi zorunlu kılarken aynı şeyi hissettiler .
Konuyla ilgili benden daha yetkin kişilerce cevaplanan ilgili bir soru burada .
nan != nan
. Bu cevabın yarı şaka olduğunu anlıyorum, ancak neden OP'ye yazıldığı gibi yardımcı olması gerektiğini anlamıyorum.
==
(ve diğer cevabı kabul ettikleri düşünüldüğünde) göz önüne alındığında , bu sadece OP'nin başlığı nasıl ifade ettiğiyle ilgili bir problem gibi görünüyor. Bu tutarsızlığı gidermek için başlığı yeniden yazdım. (@Cmaster ile aynı fikirdeyim çünkü bu cevabın ilk yarısını kasıtlı olarak geçersiz kılmak: bu sorunun sorulduğu şey bu değildi).
Bu, karmaşık çarpmanın CPython'da nasıl uygulandığına dair bir uygulama ayrıntısıdır. Diğer dillerin aksine (örneğin C veya C ++), CPython biraz basit bir yaklaşım benimser:
Py_complex
_Py_c_prod(Py_complex a, Py_complex b)
{
Py_complex r;
r.real = a.real*b.real - a.imag*b.imag;
r.imag = a.real*b.imag + a.imag*b.real;
return r;
}
Yukarıdaki kodla ilgili sorunlu bir durum şudur:
(0.0+1.0*j)*(inf+inf*j) = (0.0*inf-1*inf)+(0.0*inf+1.0*inf)j
= nan + nan*j
Ancak, -inf + inf*j
sonuç olarak sahip olmak ister .
Bu bakımdan diğer diller çok ileride değil: karmaşık sayı çarpımı uzun bir süredir C standardının bir parçası değildi, yalnızca C99'da karmaşık bir çarpmanın nasıl yapılması gerektiğini açıklayan ek G olarak dahil edildi - ve o kadar basit değil yukarıdaki okul formülü! C ++ standardı, karmaşık çarpmanın nasıl çalışması gerektiğini belirtmez, bu nedenle çoğu derleyici uygulaması, C99 ile uyumlu (gcc, clang) veya olmayabilen (MSVC) C-uygulamasına geri dönmektedir.
Yukarıdaki "sorunlu" örnek için, C99 uyumlu uygulamalar ( okul formülünden daha karmaşık olan ) beklenen sonucu verir ( canlıya bakın ):
(0.0+1.0*j)*(inf+inf*j) = -inf + inf*j
C99 standardıyla bile, tüm girişler için kesin bir sonuç tanımlanmamıştır ve C99 uyumlu versiyonlar için bile farklı olabilir.
Bir başka yan etkisi float
terfi değil complex
C99 çarparak olmasıdır inf+0.0j
ile 1.0
veya 1.0+0.0j
(burada canlı bakınız) farklı sonuçlara yol açabilir:
(inf+0.0j)*1.0 = inf+0.0j
(inf+0.0j)*(1.0+0.0j) = inf-nanj
Hayali parçası olmak -nan
değil nan
tüm sessiz nans (bkz eşdeğerdir çünkü (CPython gibi), burada bir rol oynamaz bu ) bile bazıları işareti bit kümesine sahip (ve dolayısıyla olarak basılmış, "-", bkz bu ) ve bazıları değil.Bu en azından sezgiseldir.
Benim ondan çıkaracağım anahtar nokta şudur: "basit" karmaşık sayı çarpma (veya bölme) hakkında basit bir şey yoktur ve diller ve hatta derleyiciler arasında geçiş yaparken kişinin ince hatalar / farklılıklar için kendini desteklemesi gerekir.
printf
ve benzerinin double ile nasıl çalıştığına dair bir uygulama detayıdır : "-" yazdırılıp yazdırılmayacağına karar vermek için işaret bitine bakarlar (nan olsun ya da olmasın). Yani haklısın, "nan" ve "-nan" arasında anlamlı bir fark yok, yakında cevabın bu kısmını düzeltir.
Python'dan komik tanım. Biz kalem ve kağıt ile bu çözümü ise ben beklenen sonuç olacağını söyleyebilirim expected: (inf + 0j)
biz normunu demek olduğunu biliyorum çünkü belirttiği gibi 1
bu yüzden (float('inf')+0j)*1 =should= ('inf'+0j)
:
Ama gördüğünüz gibi durum böyle değil ... çalıştırdığımızda şunu elde ederiz:
>>> Complex( float('inf') , 0j ) * 1
result: (inf + nanj)
Python bu anlayan *1
bir kompleks sayı ve değil norm olarak 1
o kadar yorumladığı bu yüzden *(1+0j)
ve biz yapmak çalıştığınızda hata görünür inf * 0j = nanj
olarak inf*0
çözülmesi mümkün değildir.
Aslında yapmak istediğiniz şey (1'in 1'in normu olduğunu varsayarsak):
z = x + iy
Gerçek x bölümü ve sanal bölümü y olan karmaşık bir sayı ise, karmaşık eşleniğinin z
şu şekilde tanımlandığını z* = x − iy
ve mutlak değerin şu şekilde tanımlandığını hatırlayın norm of z
:
Aşağıdakilere benzer bir şey yapmamızın 1
norm olduğunu varsaymak 1
:
>>> c_num = complex(float('inf'),0)
>>> value = 1
>>> realPart=(c_num.real)*value
>>> imagPart=(c_num.imag)*value
>>> complex(realPart,imagPart)
result: (inf+0j)
çok sezgisel değil biliyorum ... ama bazen kodlama dilleri günümüzde kullandığımızdan farklı bir şekilde tanımlanıyor.