(İnf + 0j) * 1 neden inf + nanj olarak değerlendirilir?


97
>>> (float('inf')+0j)*1
(inf+nanj)

Neden? Bu, kodumda kötü bir hataya neden oldu.

1Çarpımsal kimlik neden vermiyor (inf + 0j)?


1
Sanırım aradığınız anahtar kelime " alan ". Toplama ve çarpma varsayılan olarak tek bir alan içinde tanımlanır ve bu durumda kodunuzu barındırabilecek tek standart alan karmaşık sayılar alanıdır, bu nedenle işlem iyi hale gelmeden önce her iki sayının da varsayılan olarak karmaşık sayılar olarak ele alınması gerekir. tanımlı. Hangi onlar demek değildir olamazdı bu tanımları uzatmak, ancak görünüşe göre onlar sadece standart şeyle gitti ve tanımları uzatmak için kendi yolumdan gitmek için bir dürtü hissetmedim.
user541686

1
Oh, ve bu tuhaflıkları sinir bozucu buluyorsanız ve bilgisayarınızı yumruklamak istiyorsanız, sempati duyuyorsunuz .
user541686

2
@Mehrdad, bu sonlu olmayan elemanları eklediğinizde alan olmaktan çıkıyor. Nitekim artık çarpımsal nötr olmadığından, tanım gereği alan olamaz.
Paul Panzer

@PaulPanzer: Evet, sanırım bu unsurları sonradan ittiler.
user541686

1
kayan noktalı sayılar (sonsuz ve NaN'yi hariç tutsanız bile) bir alan değildir. Alanlar için tutulan kimliklerin çoğu, kayan nokta numaraları için geçerli değildir.
plugwash

Yanıtlar:


95

1İlk olarak bir karmaşık sayı dönüştürülür 1 + 0j, burada bir daha sonra açar inf * 0bir 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

8
"Neden ...?" Sorusunu yanıtlamak için, muhtemelen en önemli adım, ilk adımdır, nereye 1atılır 1 + 0j.
Warren Weckesser

5
Not Gerçek kayan nokta türleri olduğunu C99 belirtir değil karmaşık türde (taslak standardının bölüm 6.3.1.8 Körleştirilmiş) ile çarpılması zaman karmaşık terfi ve aynı bildiğim kadarıyla aynı ++ 'ın std :: karmaşık C doğrudur. Bu kısmen performans nedenleriyle olabilir, ancak aynı zamanda gereksiz NaN'leri de önler.
benrg

@benrg NumPy'de array([inf+0j])*1ayrı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?
marnix

1
@marnix bundan daha karmaşık. hemen hemen her operatörün ve işlevin türetildiği numpybir merkezi sınıfa sahiptir ufunc. ufuncdizilerle ç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 ...
Paul Panzer

1
... tam olarak eşleşmeyen türleri gerektiği gibi yükselterek. Bu verimler için 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"
Paul Panzer

32

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

Kısa cevap:

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.

Uzun cevap:

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.

Tek noktalı sıkıştırma

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.

Diğer uzantılar ...

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

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.

Bu ek G şeyi

Ş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 infve nanetkileşimde bulunmaya (esasen infkozlar 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 iyisini yapabilir miyiz?

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 nanaçı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)

Riemann kurtarmaya

Tüm bunların ışığında, belki de eski tek noktalı kompaktlaştırma yapılacak en güvenli şeydir. Belki Ek G'nin yazarları, cprojtü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 .


5
Evet, çünkü 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.
cmaster - reinstate monica

Soru gövdesindeki kodun aslında kullanmadığı ==(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).
Peter Cordes

3
@PeterCordes bu rahatsız edici olurdu çünkü kutupsal koordinatları kullanarak karmaşık çarpmayı 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. Bu benim görüşüme göre, kabul edilenden daha derin bir açıklama ve ayrıca nan! = Nan kuralında yankıları olan bir açıklama.
Paul Panzer

3
C99, gerçek kayan nokta türlerinin karmaşık bir türle çarpılırken karmaşık hale getirilmediğini belirtir (taslak standardının 6.3.1.8 bölümü) ve bildiğim kadarıyla aynısı C ++ 'ın std :: complex için de geçerlidir. Bu, 1'in bu dillerdeki bu türler için çarpımsal bir kimlik olduğu anlamına gelir. Python da aynısını yapmalı. Mevcut davranışına sadece bir hata diyebilirim.
benrg

2
@PaulPanzer: Bilmiyorum, ancak temel kavram, bir sıfırın (Z olarak adlandıracağım) her zaman x + Z = x ve x * Z = Z ve 1 / Z = NaN, bir (pozitif sonsuz küçük) 1 / P = + INF'yi, bir (negatif sonsuz küçük) 1 / N = -INF'yi destekler ve (işaretsiz sonsuz küçük) 1 / U = NaN verir. Genel olarak, xx gerçek bir tam sayı olmadıkça, xx U olacaktır, bu durumda Z
sonucunu

6

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:

  1. ints / floats, çarpmada karmaşık sayılara yükseltilir
  2. Sonsuz sayılar dahil olduğu anda istenen / beklenen sonuçları sağlamayan basit okul formülü kullanılır :
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*jsonuç 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 floatterfi değil complexC99 çarparak olmasıdır inf+0.0jile 1.0veya 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-nanjHayali parçası olmak -nandeğil nantü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.


Pek çok nan bit desen olduğunu biliyorum. Yine de işaret bit şeyini bilmiyordum. Ama anlamsal olarak -nan'ın nan'dan farkı nedir? Yoksa nan'ın nan'den farklı olduğunu mu söylemeliyim?
Paul Panzer

@PaulPanzer Bu sadece nasıl printfve 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.
Ead

Ah iyi. Bir mo için fp hakkında bildiğimi düşündüğüm her şeyin aslında doğru olmadığını düşünüyordum ...
Paul Panzer

Can sıkıcı olduğum için üzgünüm ama "hayali 1.0 olmadığından, yani 1.0j'nin çarpma açısından 0.0 + 1.0j ile aynı olmadığından emin misiniz ?" doğru? Bu ek G, tamamen hayali bir türü (G.2) belirtiyor ve aynı zamanda nasıl çarpılması gerektiğini vb. (G.5.1) belirtiyor gibi görünüyor
Paul Panzer

@PaulPanzer Hayır, sorunlara işaret ettiğiniz için teşekkür ederiz! C ++ - kodlayıcı olarak, çoğunlukla C99 standardını C ++ - camlar aracılığıyla görüyorum - aklımı kaçırdı, C burada bir adım önde - açıkçası bir kez daha haklısın.
Ead

3

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 1bu 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 *1bir kompleks sayı ve değil norm olarak 1o kadar yorumladığı bu yüzden *(1+0j)ve biz yapmak çalıştığınızda hata görünür inf * 0j = nanjolarak 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 + iyGerç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 − iyve mutlak değerin şu şekilde tanımlandığını hatırlayın norm of z:

görüntü açıklamasını buraya girin

Aşağıdakilere benzer bir şey yapmamızın 1norm 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.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.