Python turu bir sonraki en yüksek 10 güce ulaşacak


44

math.ceilBir sonraki en yüksek 10 güce bir sayı atanacak şekilde nasıl gerçekleştirebilirim ?

# 0.04  ->  0.1
# 0.7   ->  1
# 1.1   ->  10  
# 90    ->  100  
# ...

Şu anki çözümüm, giriş numarasının aralığını kontrol eden bir sözlük, ancak sabit kodlanmış ve tek katmanlı bir çözümü tercih ederim. Belki burada basit bir matematik hile veya karşılık gelen bir numpy işlevi eksik?


3
@bold, bu çözümler 10yukarıdan çalışıyor gibi görünüyor , bunun örneğin bir şeye ihtiyacı olacak log10.
jonrsharpe

3
İstediğiniz kelime "güç" dür. Belki de anadilinizde anadiliniz için yanlış çeviri yapmışsınızdır.
user2357112 Monica

Teşekkürler, Monica! @bold: Bu soruyu buldum, ancak bu farklı bir sorun. Jonrsharpe mükemmel bir cevap verdi
offeltoffel

2
Bu aynı zamanda büyüklük sırası ile de ilgilidir . 1 0.
Sırada

Yanıtlar:


60

Bunu yapmak için math.ceilile kullanabilirsiniz math.log10:

>>> 10 ** math.ceil(math.log10(0.04))
0.1
>>> 10 ** math.ceil(math.log10(0.7))
1
>>> 10 ** math.ceil(math.log10(1.1))
10
>>> 10 ** math.ceil(math.log10(90))
100

log10(n)size xtatmin edici bir çözüm sunar 10 ** x == n, böylece xyuvarlarsanız, bir sonraki en yüksek 10 gücün üssünü verir.

Not Bir değer için o zaten bir tamsayı, "10 sonraki en yüksek güç" olacak :nxn

>>> 10 ** math.ceil(math.log10(0.1))
0.1
>>> 10 ** math.ceil(math.log10(1))
1
>>> 10 ** math.ceil(math.log10(10))
10

1
Günlük işlevi ile çalışmak sadece gelemediğim hile gibi görünüyor. Tam da umduğum gibi olduğuna inanıyorum! Thanks a lot
offeltoffel

2
Not: İstediğiniz davranışa bağlı olarak, bu 10'luk güçler için çalışmaz, örneğin 10 ** math.ceil(math.log10(1)) == 1, "bir sonraki en yüksek güç" değildir
Cireo

5
Not: Bu cevap kayan nokta aritmetiğine dayanır ve bu nedenle yuvarlama hataları nedeniyle başarısız olabilir. Örneğin 1000000000000001'de beslemeyi deneyin.
plugwash

2
@plugwash gerekli değildir, matematik işlevleri de örneğin ondalık kabul eder.
jonrsharpe

5
Evet başka türler de iletebilirsiniz, ancak bunlar çift kesinlikli kayar nokta sayısına dönüştürülecek ve C "log10" işlevine aktarılacaktır. Büyük sayıların taşmasını önlemek için özel bir durum vardır, ancak yuvarlama hatalarını önlemek için hiçbir şey yoktur.
plugwash

21

Sorununuz yetersiz, geri çekilmeli ve bazı sorular sormalısınız.

  • Girdileriniz nelerdir?
  • Çıktılarınız için ne tür (ler) istiyorsun?
  • 1'den küçük sonuçlar için tam olarak neye dönmek istiyorsunuz? 10'luk gerçek güçler mi, yoksa 10'luk güçlerin kayan nokta yaklaşımı mı istiyorsunuz? 10'luk negatif güçlerin kayan noktada tam olarak ifade edilemeyeceğinin farkındasınız değil mi? Şimdilik 10 kuvvetin kayan nokta yaklaşımını istediğinizi varsayalım.
  • Giriş tam olarak 10'luk bir güçse (veya 10'luk bir gücün en yakın kayan nokta yaklaşımı), çıkış girişle aynı mı olmalıdır? Yoksa 10'luk bir sonraki güç mü olmalı? "10 -> 10" veya "10 -> 100"? Şimdilik ilkini varsayalım.
  • Girdi değerleriniz söz konusu türlerin olası herhangi bir değeri olabilir mi? yoksa daha kısıtlılar mı?

Başka bir cevapta logaritmanın alınması, daha sonra yuvarlanması (tavan fonksiyonu) ve daha sonra üssel hale getirilmesi önerildi.

def nextpow10(n):
    return 10 ** math.ceil(math.log10(n))

Ne yazık ki bu yuvarlama hatalarından muzdarip. Her şeyden önce n, sahip olduğu veri türünden çift hassasiyetli kayan nokta numarasına dönüştürülür ve potansiyel olarak yuvarlama hataları getirir, ardından logaritma, hem dahili hesaplamalarında hem de sonuçta potansiyel olarak daha fazla yuvarlama hatası getirerek hesaplanır.

Bu nedenle, yanlış bir sonuç verdiği bir örnek bulmak uzun sürmedi.

>>> import math
>>> from numpy import nextafter
>>> n = 1
>>> while (10 ** math.ceil(math.log10(nextafter(n,math.inf)))) > n:
...     n *= 10
... 
>>> n
10
>>> nextafter(n,math.inf)
10.000000000000002
>>> 10 ** math.ceil(math.log10(10.000000000000002))
10

Teorik olarak diğer yönde başarısız olması da mümkündür, ancak bu provoke etmek çok daha zor görünmektedir.

Dolayısıyla, şamandıralar ve ints için sağlam bir çözüm için logaritmamızın değerinin sadece yaklaşık olduğunu varsaymalıyız ve bu nedenle birkaç olasılığı test etmeliyiz. Çizgisinde bir şey

def nextpow10(n):
    p = round(math.log10(n))
    r = 10 ** p
    if r < n:
        r = 10 ** (p+1) 
    return r;

Bu kodun, makul bir gerçek dünya büyüklüğündeki tüm argümanlar için doğru sonuçlar vermesi gerektiğine inanıyorum. Çok küçük veya çok sayıda tamsayı olmayan ve kayan nokta tipi için kayan noktaya dönüştürülen sorunlar nedeniyle kırılacaktır. Python özel durumları taşmayı önlemek amacıyla log10 işlevine tamsayı bağımsız değişkenleri sağlar, ancak yine de yeterince büyük bir tamsayı ile yuvarlama hataları nedeniyle yanlış sonuçları zorlamak mümkün olabilir.

İki uygulamayı test etmek için aşağıdaki test programını kullandım.

n = -323 # 10**-324 == 0
while n < 1000:
    v = 10 ** n
    if v != nextpow10(v): print(str(v)+" bad")
    try:
        v = min(nextafter(v,math.inf),v+1)
    except:
        v += 1
    if v > nextpow10(v): print(str(v)+" bad")
    n += 1

Bu, saf uygulamada çok sayıda hata bulur, ancak geliştirilmiş uygulamada hiçbir hata yoktur.


Burada daha fazla ayrıntıya girme çabanız için teşekkür ederiz. Jonrsharpe'in cevabı sorunumu zaten çözmüş olsa da, bu cevap benzer ama daha özel soruları olan diğerleri için yararlı olabilir.
offeltoffel

1
Neden roundyerine kullanıyorsun math.ceil? Bu r < n, doğru olan birçok gereksiz durumu tanıtacaktır ve bu nedenle ek iş yapması gerekir.
a_guest

1
Çünkü günlük her iki yönde de kapalı olabilir.
plugwash

1
"geliştirilmiş" kodunu kullanarak ama yuvarlak yüksek ucunda düşük ucunda ve 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 üzerine 1e-317 için başarısızlıklarında math.ceil sonuçlarla yerini ile.
plugwash

1
(pratikte olsa muhtemelen iyi)
plugwash

3

Görünüşe göre 10'un en düşük sonraki gücünü istiyorsun ... İşte saf matematik ve günlük yok, özyineleme kullanmanın bir yolu.

def ceiling10(x):
    if (x > 10):
        return ceiling10(x / 10) * 10
    else:
        if (x <= 1):
            return ceiling10(10 * x) / 10
        else:
            return 10
for x in [1 / 1235, 0.5, 1, 3, 10, 125, 12345]:
    print(x, ceiling10(x))

Sadece bunu test ettim, çoğu pratik durumda iyi çalışıyor gibi görünüyor, ancak yeterince küçük girdilerle yuvarlama hatalarından muzdarip gibi görünüyor. ceiling10 (1e-6) 1.0000000000000002e-06 verir
plugwash

0
y = math.ceil(x)
z = y + (10 - (y % 10))

Belki böyle bir şey? Sadece kafamın üstünde ama terminalde birkaç sayı denediğimde işe yaradı.


0

Şuna bir bak!

>>> i = 0.04123; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )               
0.04123 0.1
>>> i = 0.712; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                 
0.712 1
>>> i = 1.1; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                   
1.1 10
>>> i = 90; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                    
90 100

Bu kod onlarca gücün prensibine dayanmaktadır len( str( int( float_number ) ) ).

4 vaka var:

    1. int( i ) > 1.

    Floatnumarası - dönüştürülür intdaha sonra dize, str()ondan, bir bize verecektir stringile lengthtam olarak aradığınız olan. Yani, ilk bölüm, giriş için i > 1.0- 10bu uzunluğun on gücüdür.

    1. & 3. Küçük dallanma: i > 1.0ve i > 0.1<=> sırasıyla 10ve 1.
    1. Ve son dava, ne zaman i < 0.1: Burada, on olumsuz güçte olacaktır. Virgülden sonra ilk sıfır olmayan öğeyi elde etmek için ("%.100f" % i ).replace('.','').index( k ), k [1:10]aralığının geçtiği böyle bir yapı kullandım . Daha sonra, minimum sonuç listesini alın. Ve bir azalır, ilk sıfırdır, sayılacak. Ayrıca, burada standart piton en index()ondan sıfır olmayan elemanın en az birini bulamazsınız eğer çökebilir [1:10]sonunda ben "filtre" oluşumu ile listeleme gerekir nedeni budur aralık,: if str( j ) in "%.100f" % i. Ayrıca, daha derine inmek - %.100ffarklı alınabilir.

Lütfen biraz açıklama ekleyin.
Mobin Ranjbar
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.