Python tròn tới sức mạnh cao nhất tiếp theo là 10


44

Làm thế nào tôi có thể quản lý để thực hiện math.ceilsao cho một số được gán cho công suất cao nhất tiếp theo là 10?

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

Giải pháp hiện tại của tôi là một từ điển kiểm tra phạm vi của số đầu vào, nhưng nó được mã hóa cứng và tôi thích một giải pháp một lớp. Có lẽ tôi đang thiếu một thủ thuật toán học đơn giản hoặc một hàm numpy tương ứng ở đây?


3
@bold có vẻ như các giải pháp đó hoạt động từ 10trên xuống, điều này sẽ cần một cái gì đó với ví dụ log10.
jonrsharpe

3
Từ bạn muốn là "sức mạnh". Có lẽ bạn đã dịch sai từ của ngôn ngữ mẹ đẻ của bạn cho nó.
user2357112 hỗ trợ Monica

Cảm ơn, Monica! @bold: Tôi tìm thấy câu hỏi này, nhưng đó là một vấn đề khác. Jonrsharpe đã cung cấp một câu trả lời hoàn hảo
offeltoffel

2
Điều này cũng liên quan đến thứ tự cường độ . 1 là thứ tự 0, 10 là đơn hàng thứ 1, 100 là đơn hàng thứ 2, v.v.
wjandrea

Câu trả lời:


60

Bạn có thể sử dụng math.ceilvới math.log10để làm điều này:

>>> 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)cung cấp cho bạn giải pháp xthỏa mãn 10 ** x == n, vì vậy nếu bạn làm tròn xnó sẽ cung cấp cho bạn số mũ cho công suất cao nhất tiếp theo là 10.

Lưu ý rằng đối với một giá trị nxđã là một số nguyên, "bên cạnh quyền lực cao nhất của 10" sẽ là 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
Làm việc với chức năng đăng nhập dường như chỉ là mẹo mà tôi không thể nghĩ ra. Tôi tin rằng đây là chính xác những gì tôi đã hy vọng! Cảm ơn rất nhiều
offeltoffel

2
Lưu ý: tùy thuộc vào hành vi mong muốn của bạn, điều này không hoạt động với quyền hạn 10, ví dụ 10 ** math.ceil(math.log10(1)) == 1, đó không phải là "quyền lực cao nhất tiếp theo"
Cireo

5
Lưu ý: câu trả lời này dựa trên số học dấu phẩy động và như vậy nó có thể thất bại do lỗi làm tròn. Thử cho ăn trong 1000000000000001 chẳng hạn.
plugwash

2
@plugwash không nhất thiết, các hàm toán học cũng sẽ chấp nhận ví dụ: binary.Decimals.
jonrsharpe

5
Có, bạn có thể vượt qua các loại khác, nhưng chúng sẽ được chuyển đổi thành số dấu phẩy động chính xác kép và được chuyển đến hàm C "log10". Có một trường hợp đặc biệt để ngăn chặn các bản ghi của số lượng lớn tràn ra, nhưng không có gì để ngăn ngừa lỗi làm tròn.
plugwash

21

Vấn đề của bạn chưa được chỉ định rõ ràng, bạn cần lùi lại và đặt một số câu hỏi.

  • Loại đầu vào của bạn là gì?
  • Loại nào bạn muốn cho đầu ra của bạn?
  • Đối với kết quả nhỏ hơn 1, chính xác bạn muốn làm tròn để làm gì? Bạn có muốn quyền hạn thực tế của 10 hoặc xấp xỉ điểm gần đúng của quyền hạn 10 không? Bạn có biết rằng sức mạnh tiêu cực của 10 không thể được thể hiện chính xác trong dấu phẩy động phải không? Bây giờ chúng ta hãy giả sử rằng bạn muốn xấp xỉ điểm lũy thừa của lũy thừa 10.
  • Nếu đầu vào chính xác là công suất 10 (hoặc xấp xỉ điểm nổi gần nhất của công suất 10), thì đầu ra có giống với đầu vào không? Hay nó nên là sức mạnh tiếp theo của 10 lên? "10 -> 10" hoặc "10 -> 100"? Bây giờ hãy giả sử trước đây.
  • Giá trị đầu vào của bạn có thể là bất kỳ giá trị có thể có của các loại trong câu hỏi không? hoặc là họ hạn chế hơn.

Trong một câu trả lời khác, nó được đề xuất lấy logarit, sau đó làm tròn lên (hàm trần), sau đó lũy thừa.

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

Thật không may, điều này bị lỗi làm tròn. Trước hết n được chuyển đổi từ bất kỳ loại dữ liệu nào mà nó có thành số dấu phẩy động chính xác kép, có khả năng gây ra lỗi làm tròn, sau đó logarit được tính có khả năng đưa ra nhiều lỗi làm tròn hơn cả trong tính toán bên trong và kết quả của nó.

Như vậy, tôi không mất nhiều thời gian để tìm một ví dụ mà nó cho kết quả không chính xác.

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

Về mặt lý thuyết cũng có thể khiến nó thất bại theo hướng khác, mặc dù điều này dường như khó khiêu khích hơn nhiều.

Vì vậy, để có một giải pháp mạnh mẽ cho float và ints, chúng ta cần giả định rằng giá trị của logarit của chúng ta chỉ là gần đúng và do đó chúng ta phải kiểm tra một vài khả năng. Một cái gì đó dọc theo dòng

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

Tôi tin rằng mã này sẽ cho kết quả chính xác cho tất cả các đối số trong phạm vi độ lớn trong thế giới thực. Nó sẽ phá vỡ đối với số lượng rất nhỏ hoặc rất lớn các loại điểm không nguyên và không dấu phẩy do các vấn đề chuyển đổi chúng thành dấu phẩy động. Các trường hợp đặc biệt của Python đối số nguyên cho hàm log10 trong nỗ lực ngăn chặn tràn, nhưng với một số nguyên đủ lớn, có thể buộc các kết quả không chính xác do lỗi làm tròn.

Để kiểm tra hai triển khai tôi đã sử dụng chương trình thử nghiệm sau.

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

Điều này tìm thấy rất nhiều thất bại trong việc thực hiện ngây thơ, nhưng không có gì trong việc thực hiện được cải thiện.


Cảm ơn bạn đã nỗ lực để đi sâu vào chi tiết ở đây. Trong khi câu trả lời của jonrsharpe đã giải quyết vấn đề của tôi, câu trả lời này có thể hữu ích cho những người khác có câu hỏi tương tự nhưng cụ thể hơn.
offeltoffel

1
Tại sao bạn sử dụng roundthay vì math.ceil? Điều này sẽ giới thiệu rất nhiều trường hợp không cần thiết r < nlà đúng và vì vậy nó cần phải thực hiện công việc bổ sung.
a_guest

1
Bởi vì nhật ký có thể được tắt theo một trong hai hướng.
cắm

1
sử dụng mã "cải tiến" nhưng với vòng được thay thế bằng math.ce
cắm

1
(trong thực tế có lẽ nó vẫn ổn)
plugwash

3

Có vẻ như bạn muốn sức mạnh tiếp theo thấp nhất là 10 ... Đây là một cách sử dụng toán học thuần túy và không có nhật ký, nhưng đệ quy.

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

Chỉ cần thử nghiệm cái này, tôi sẽ cho nó một upvote vì nó dường như hoạt động tốt trong hầu hết các trường hợp thực tế, nhưng nó dường như bị lỗi làm tròn với đầu vào đủ nhỏ. trần10 (1e-6) cho 1.0000000000000002e-06
plugwash

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

Một cái gì đó như thế này có thể? Nó chỉ ở trên đỉnh đầu của tôi nhưng nó hoạt động khi tôi thử một vài số trong thiết bị đầu cuối.


0

Kiểm tra này!

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

Mã này dựa trên hiệu trưởng của quyền lực mười len( str( int( float_number ) ) ).

Có 4 trường hợp:

    1. int( i ) > 1.

    Floatsố - chuyển đổi sang int, sau đó chuỗi str()từ nó, sẽ cho chúng ta một stringvới lengthđó là chúng ta đang tìm kiếm chính xác. Vì vậy, phần đầu tiên, cho đầu vào i > 1.0- nó có 10sức mạnh mười độ dài này.

    1. & 3. Phân nhánh nhỏ: i > 1.0i > 0.1<=> nó là 101tương ứng.
    1. Và trường hợp cuối cùng, khi i < 0.1: Ở đây, mười sẽ có sức mạnh tiêu cực. Để có được phần tử khác không đầu tiên sau dấu phẩy, tôi đã sử dụng cấu trúc như vậy ("%.100f" % i ).replace('.','').index( k ), trong đó k chạy trên [1:10]khoảng. Sau đó, lấy tối thiểu danh sách kết quả. Và giảm đi một, nó là số 0 đầu tiên, sẽ được tính. Ngoài ra, ở đây, trăn tiêu chuẩn index()có thể gặp sự cố, nếu nó không tìm thấy ít nhất một phần tử khác không từ [1:10]khoảng thời gian, đó là lý do tại sao cuối cùng tôi phải "lọc" danh sách khi xảy ra : if str( j ) in "%.100f" % i. Ngoài ra, để có được độ chính xác sâu hơn - %.100fcó thể được thực hiện khác nhau.

Xin vui lòng thêm một số giải thích.
Mobin Ranjbar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.