2012-12-22 41 views
7

Trong kịch bản tôi đang viết, tôi muốn tìm chiều dài của một Fixnum trong Ruby. Tôi có thể làm <num>.to_s.length, nhưng có cách nào để trực tiếp tìm thấy chiều dài của một Fixnum mà không cần chuyển đổi nó thành một String?Làm thế nào để xác định độ dài của một Fixnum trong Ruby?

+0

"chiều dài của một' Fixnum' "là gì? Trong những gì đại diện? –

+0

@ JörgWMittag: Có bao nhiêu chữ số trong đó. – Orcris

Trả lời

23
puts Math.log10(1234).to_i + 1 # => 4 

Bạn có thể thêm nó vào Fixnum như thế này:

class Fixnum 
    def num_digits 
    Math.log10(self).to_i + 1 
    end 
end 

puts 1234.num_digits # => 4 
+11

Mỗi lần tôi viết một cái gì đó, tôi đều ngạc nhiên. Trong MRI 1.9.3, 'n.to_s.length' nhanh hơn đối với bất kỳ số nguyên nào được biểu thị bằng một bảng Fixnum. Một _lot_ nhanh hơn: Trên hộp của tôi, 'n.to_s.length' mất một nơi nào đó giữa một phần ba và một nửa thời gian của phương pháp logarit, tùy thuộc vào độ dài của số. Nếu số đó phải được biểu diễn một Bignum, thì phương thức logarit bắt đầu thắng. Cả hai phương pháp này rất nhanh, mặc dù, khoảng 6 mili giây (đối với phương thức logarit), và giữa 0,2 và 0,3 mili giây (đối với phương thức chuỗi). –

+0

@WayneConrad: Âm thanh như 'Math.log10' phải có triển khai khá kém hiệu quả. Tôi vừa thử một phương thức đơn giản đi qua một bảng gồm tất cả các quyền hạn của 10 mà vừa với 32/64 bit, và thực hiện một phép so sánh '> =' cho mỗi cái - đó là một liên lạc nhanh hơn 'Math.log10', nhưng vẫn chậm hơn 'to_s'. Nó có thể được thực hiện nhanh hơn bằng cách "unrolling" một tìm kiếm nhị phân của cùng một bảng, giống như unrolling một vòng lặp (sau đó bảng sẽ không cần thiết nữa - những con số tương tự sẽ được mã hóa cứng thành một loạt các điều kiện). –

+4

Xem ra. Điều này làm tăng lỗi cho các số không dương. – sawa

0

Nếu bạn không muốn sử dụng regex, bạn có thể sử dụng phương pháp này:

def self.is_number(string_to_test) 
is_number = false 
# use to_f to handle float value and to_i for int 
string_to_compare = string_to_test.to_i.to_s 
string_to_compare_handle_end = string_to_test.to_i 

# string has to be the same 
if(string_to_compare == string_to_test) 
    is_number = true 
end 
# length for fixnum in ruby 
size = Math.log10(string_to_compare_handle_end).to_i + 1 
# size has to be the same 
if(size != string_to_test.length) 
    is_number = false 
end 
is_number 
end 
1

Một cách khác:

def ndigits(n) 
    n=n.abs 
    (1..1.0/0).each { |i| return i if (n /= 10).zero? } 
end 

ndigits(1234) # => 4 
ndigits(0) # => 1 
ndigits(-123) # => 3 
4

Mặc dù vòng lặp được bỏ phiếu hàng đầu là tốt đẹp, nó không phải là rất Ruby và sẽ được làm chậm cho số lượng lớn, các .to_s là một chức năng được xây dựng trong và do đó sẽ nhanh hơn nhiều. Hàm ALMOST được tích hợp sẵn sẽ nhanh hơn nhiều so với các vòng lặp hoặc vòng lặp được xây dựng.

5

Ruby 2.4 có phương thức Integer#digits, trả về một mảng chứa các chữ số.

num = 123456 
num.digits 
# => [6, 5, 4, 3, 2, 1] 
num.digits.count 
# => 6 

EDIT:

Để xử lý số âm (nhờ @MatzFan), sử dụng giá trị tuyệt đối. Integer#abs

-123456.abs.digits 
# => [6, 5, 4, 3, 2, 1] 
+0

..if nó là một số nguyên dương, nếu không 'Math :: DomainError'. Ruby còn nhiều hơn thế nữa – MatzFan

0

Sidenote cho của Ruby 2.4+

Tôi chạy một số tiêu chuẩn về các giải pháp khác nhau, và Math.log10(x).to_i + 1 thực sự là nhanh hơn rất nhiều so vớix.to_s.length. comment from @Wayne Conrad đã lỗi thời. Các new solution with digits.count là trailing xa phía sau, đặc biệt là với số lượng lớn hơn:

with_10_digits = 2_040_240_420 

print Benchmark.measure { 1_000_000.times { Math.log10(with_10_digits).to_i + 1 } } 
# => 0.100000 0.000000 0.100000 ( 0.109846) 
print Benchmark.measure { 1_000_000.times { with_10_digits.to_s.length } } 
# => 0.360000 0.000000 0.360000 ( 0.362604) 
print Benchmark.measure { 1_000_000.times { with_10_digits.digits.count } } 
# => 0.690000 0.020000 0.710000 ( 0.717554) 

with_42_digits = 750_325_442_042_020_572_057_420_745_037_450_237_570_322 

print Benchmark.measure { 1_000_000.times { Math.log10(with_42_digits).to_i + 1 } } 
# => 0.140000 0.000000 0.140000 ( 0.142757) 
print Benchmark.measure { 1_000_000.times { with_42_digits.to_s.length } } 
# => 1.180000 0.000000 1.180000 ( 1.186603) 
print Benchmark.measure { 1_000_000.times { with_42_digits.digits.count } } 
# => 8.480000 0.040000 8.520000 ( 8.577174) 
Các vấn đề liên quan