Python ปัดเศษเป็นพลังงานสูงสุดถัดไปที่ 10


44

ฉันจะจัดการในการดำเนินการmath.ceilดังกล่าวว่าเป็นจำนวนที่ได้รับมอบหมายให้อำนาจสูงสุดต่อไปของ 10?

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

โซลูชันปัจจุบันของฉันคือพจนานุกรมที่ตรวจสอบช่วงของหมายเลขอินพุต แต่มันเป็นรหัสฮาร์ดคอร์และฉันต้องการโซลูชันแบบซับเดียว บางทีฉันอาจขาดเล่ห์เหลี่ยมทางคณิตศาสตร์อย่างง่ายหรือฟังก์ชัน numpy ที่เกี่ยวข้องที่นี่


3
@bold ดูเหมือนว่าโซลูชันเหล่านี้ทำงาน10ได้log10ดี
jonrsharpe

3
คำที่คุณต้องการคือ "พลัง" บางทีคุณอาจได้รับการแปลที่ผิดสำหรับคำในภาษาของคุณ
user2357112 รองรับ Monica

ขอบคุณโมนิก้า! @ ทอง: ฉันพบคำถามนี้ แต่มันเป็นปัญหาที่แตกต่าง Jonrsharpe ได้ให้คำตอบที่สมบูรณ์แบบ
offeltoffel

2
นี้ยังเป็นที่เกี่ยวข้องกับการลำดับความสำคัญ 1 คือลำดับที่ 0, 10 คือลำดับที่ 1, 100 คือลำดับที่ 2, ฯลฯ
wjandrea

คำตอบ:


60

คุณสามารถใช้math.ceilกับ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)ให้คำตอบxที่ตรงกับความต้องการของ10 ** x == nคุณดังนั้นหากคุณปัดเศษขึ้นxจะให้เลขชี้กำลังสำหรับกำลังสูงสุด 10 อันดับถัดไป

โปรดทราบว่าสำหรับค่าnที่xเป็นจำนวนเต็มอยู่แล้ว"กำลังสูงสุดต่อไปของ 10"จะเป็นn:

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

1
การทำงานกับฟังก์ชั่นบันทึกดูเหมือนจะเป็นเพียงเคล็ดลับที่ฉันไม่สามารถทำได้ ฉันเชื่อว่านี่คือสิ่งที่ฉันหวังไว้! ขอบคุณมาก
offeltoffel

2
หมายเหตุ: ขึ้นอยู่กับพฤติกรรมที่คุณต้องการสิ่งนี้ไม่ได้ผลกับพลังของ 10 เช่น10 ** math.ceil(math.log10(1)) == 1ซึ่งไม่ใช่ "พลังสูงสุดต่อไป"
Cireo

5
หมายเหตุ: คำตอบนี้ขึ้นอยู่กับการคำนวณเลขทศนิยมและเช่นนั้นอาจล้มเหลวเนื่องจากข้อผิดพลาดในการปัดเศษ ลองให้อาหารใน 1000000000000001 เช่น
plugwash

2
@plugwash ไม่จำเป็นต้องใช้ฟังก์ชันทางคณิตศาสตร์ก็จะยอมรับเช่นทศนิยม
jonrsharpe

5
ใช่คุณสามารถผ่านประเภทอื่น ๆ แต่พวกเขาจะถูกแปลงเป็นจำนวนจุดลอยตัวความแม่นยำสองครั้งและส่งผ่านไปยังฟังก์ชั่น C "log10" มีกรณีพิเศษเพื่อป้องกันการบันทึกจำนวนมากล้น แต่ไม่มีอะไรที่จะป้องกันข้อผิดพลาดในการปัดเศษ
plugwash

21

ปัญหาของคุณอยู่ภายใต้การระบุคุณต้องถอยกลับและถามคำถาม

  • อินพุตของคุณประเภทใด
  • คุณต้องการผลงานประเภทใด
  • สำหรับผลลัพธ์ที่น้อยกว่า 1 คุณต้องการปัดเศษเป็นอย่างไร คุณต้องการอำนาจที่แท้จริงของ 10 หรือการประมาณจุดลอยตัวของอำนาจของ 10? คุณตระหนักดีว่าพลังเชิงลบ 10 ไม่สามารถแสดงได้อย่างแน่นอนในจุดลอยตัวใช่มั้ย สมมติว่าตอนนี้คุณต้องการการประมาณค่าจุดลอยตัวของกำลัง 10
  • หากอินพุตนั้นมีกำลังเท่ากับ 10 (หรือประมาณจุดลอยตัวที่ใกล้เคียงที่สุดของกำลัง 10) เอาต์พุตควรเหมือนกันกับอินพุตหรือไม่? หรือมันควรจะเป็นพลังงานต่อไปของ 10 ขึ้นไป? "10 -> 10" หรือ "10 -> 100"? สมมติว่าตอนนี้เป็นอดีต
  • ค่าอินพุตของคุณสามารถเป็นค่าที่เป็นไปได้ของประเภทที่เป็นปัญหาหรือไม่ หรือพวกเขาถูก จำกัด มากขึ้น

ในอีกคำตอบหนึ่งก็เสนอให้ใช้ค่าลอการิทึมจากนั้นปัดเศษ (ฟังก์ชันเพดาน) แล้วยกกำลัง

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

น่าเสียดายที่นี่มีข้อผิดพลาดในการปัดเศษ ประการแรก n ทั้งหมดจะถูกแปลงจากชนิดข้อมูลใด ๆ ที่เกิดขึ้นให้เป็นเลขทศนิยมที่มีความแม่นยำสองเท่าซึ่งอาจทำให้เกิดข้อผิดพลาดในการปัดเศษจากนั้นการคำนวณลอการิทึมจะแนะนำข้อผิดพลาดในการปัดเศษเพิ่มเติมทั้งในการคำนวณภายใน

เช่นนี้ฉันใช้เวลาไม่นานในการหาตัวอย่างที่ให้ผลลัพธ์ที่ไม่ถูกต้อง

>>> 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

นอกจากนี้ยังเป็นไปได้ในทางทฤษฎีที่มันจะล้มเหลวในทิศทางอื่นแม้ว่ามันจะยากกว่าที่จะกระตุ้น

ดังนั้นสำหรับวิธีแก้ปัญหาที่มีประสิทธิภาพสำหรับการลอยตัวและ ints เราจำเป็นต้องสมมติว่าค่าลอการิทึมของเรานั้นเป็นค่าโดยประมาณเท่านั้นและเราต้องทดสอบความเป็นไปได้สองอย่าง บางสิ่งบางอย่างตามแนวของ

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

ฉันเชื่อว่ารหัสนี้ควรให้ผลลัพธ์ที่ถูกต้องสำหรับข้อโต้แย้งทั้งหมดในขอบเขตที่แท้จริงของโลก มันจะแตกสำหรับจำนวนจุดที่ไม่ใช่จำนวนเต็มและไม่ลอยจำนวนมากหรือเล็กมากเนื่องจากปัญหาในการแปลงเป็นจุดลอย งูใหญ่กรณีพิเศษอาร์กิวเมนต์จำนวนเต็มไปยังฟังก์ชั่น log10 ในความพยายามที่จะป้องกันไม่ให้ล้น แต่ก็ยังมีจำนวนเต็มขนาดใหญ่เพียงพอก็อาจเป็นไปได้ที่จะบังคับให้ผลลัพธ์ที่ไม่ถูกต้องเนื่องจากข้อผิดพลาดในการปัดเศษ

เพื่อทดสอบการใช้งานสองแบบฉันใช้โปรแกรมทดสอบต่อไปนี้

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

สิ่งนี้พบความล้มเหลวมากมายในการใช้งานแบบไร้เดียงสา แต่ไม่มีในการใช้งานที่ปรับปรุงใหม่


ขอบคุณสำหรับความพยายามที่จะลงรายละเอียดมากขึ้นที่นี่ ในขณะที่คำตอบของ jonrsharpe ได้แก้ไขปัญหาของฉันไปแล้วคำตอบนี้อาจเป็นประโยชน์สำหรับผู้อื่นที่มีคำถามคล้ายกัน แต่มีความเฉพาะเจาะจงมากกว่า
offeltoffel

1
ทำไมคุณถึงใช้roundแทนmath.ceil? นี้จะแนะนำกรณีที่ไม่จำเป็นจำนวนมากที่r < nเป็นจริงและดังนั้นจึงจำเป็นต้องทำงานเพิ่มเติม
a_guest

1
เนื่องจากบันทึกอาจถูกปิดในทิศทางใดทิศทางหนึ่ง
plugwash

1
ใช้รหัส "ปรับปรุง"
plugwash

1
(ในทางปฏิบัติมันอาจจะใช้ได้)
plugwash

3

ดูเหมือนว่าคุณต้องการอำนาจต่อไปต่ำสุดที่ 10 ... นี่คือวิธีการใช้คณิตศาสตร์บริสุทธิ์และไม่มีบันทึก แต่เรียกซ้ำ

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))

เพียงแค่ทดสอบอันนี้ฉันให้ upvote เพราะดูเหมือนว่าจะทำงานได้ดีในกรณีที่ใช้งานได้จริง แต่ดูเหมือนว่าจะเกิดข้อผิดพลาดในการปัดเศษด้วยอินพุตที่เพียงพอ ceiling10 (1e-6) ให้ 1.0000000000000002e-06
plugwash

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

อย่างนี้อาจจะ? มันอยู่ด้านบนของหัวของฉัน แต่มันก็ใช้ได้เมื่อฉันลองเลขสองสามตัวในเทอร์มินัล


0

ลองดู!

>>> 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

len( str( int( float_number ) ) )รหัสนี้อยู่บนพื้นฐานหลักของการใช้พลังงานในสิบ

มี 4 กรณี:

    1. int( i ) > 1.

    Floatจำนวน - แปลงintสตริงหลังจากนั้นstr()จากมันจะทำให้เราstringมีlengthซึ่งเป็นเรากำลังมองว่า ดังนั้นส่วนแรกสำหรับอินพุตi > 1.0- มันคือกำลังสิบ10ของความยาวนี้

    1. & 3. การแตกกิ่งน้อย: i > 1.0และi > 0.1<=> เป็น10และ1ตามลำดับ
    1. และกรณีสุดท้ายเมื่อi < 0.1: ที่นี่สิบจะอยู่ในอำนาจเชิงลบ เพื่อให้ได้องค์ประกอบที่ไม่ใช่ศูนย์แรกหลังจากคอมมาฉันได้ใช้โครงสร้างดังกล่าว("%.100f" % i ).replace('.','').index( k )โดยที่ k วิ่งผ่าน[1:10]ช่วงเวลา หลังจากนั้นใช้รายการผลลัพธ์ขั้นต่ำ และลดลงหนึ่งมันเป็นศูนย์แรกซึ่งจะถูกนับ นอกจากนี้ที่นี่มาตรฐานหลามindex()อาจมีปัญหาถ้ามันจะไม่พบอย่างน้อยหนึ่งที่ไม่ใช่ศูนย์องค์ประกอบจาก[1:10]ช่วงเวลาที่เป็นเหตุให้ในท้ายที่สุดผมต้อง "กรอง" if str( j ) in "%.100f" % iรายการโดยเกิดขึ้น: นอกจากนี้เพื่อให้ได้ความแม่นยำที่ลึกกว่า - %.100fอาจมีความแตกต่างกัน

โปรดอธิบายให้หน่อย
Mobin Ranjbar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.