Насколько медленнее строки, содержащие числа, по сравнению с числами? - PullRequest
3 голосов
/ 22 июня 2011

Скажем, я хочу взять число и вернуть его цифры в виде массива в Ruby.
Для этой конкретной цели или для строковых функций и числовых функций в целом, что быстрее?

ЭтоЯ предполагаю, что наиболее часто используемые алгоритмы:

Использование строк: n.to_s.split(//).map {|x| x.to_i}

Использование чисел:

array = []
until n = 0
    m = n % 10
    array.unshift(m)
    n /= 10 
end

Ответы [ 3 ]

8 голосов
/ 22 июня 2011

Разница, по-видимому, составляет менее одного порядка, при этом целочисленный подход быстрее на Fixnum с. Для Bignum с относительная производительность начинается более или менее равномерно, а строковый подход значительно выигрывает при увеличении числа цифр.

в виде строк

Программа

#!/usr/bin/env ruby

require 'profile'

$n = 1234567890
10000.times do
    $n.to_s.split(//).map {|x| x.to_i}
end

выход

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 55.64     0.74      0.74    10000     0.07     0.10  Array#map
 21.05     1.02      0.28   100000     0.00     0.00  String#to_i
 10.53     1.16      0.14        1   140.00  1330.00  Integer#times
  7.52     1.26      0.10    10000     0.01     0.01  String#split
  5.26     1.33      0.07    10000     0.01     0.01  Fixnum#to_s
  0.00     1.33      0.00        1     0.00  1330.00  #toplevel

как целые числа

Программа

#!/usr/bin/env ruby

require 'profile'

$n = 1234567890
10000.times do
    array = []
    n = $n
    until n == 0
        m = n%10
        array.unshift(m)
        n /= 10
    end
    array
end

выход

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 70.64     0.77      0.77        1   770.00  1090.00  Integer#times
 29.36     1.09      0.32   100000     0.00     0.00  Array#unshift
  0.00     1.09      0.00        1     0.00  1090.00  #toplevel

Добавление

Шаблон, похоже, подходит и для меньших чисел. При $n = 12345 для строкового подхода было около 800 мс, а для целочисленного подхода - 550 мс.

Когда я пересек границу в Bignum с, скажем, с $n = 12345678901234567890, я получил 2375 мс для обоих подходов. Казалось бы, разница хорошо выравнивается, и я бы сказал, что внутреннее локальное питание Bignum имеет форму строки. Тем не менее, документация , похоже, предлагает иное.

В академических целях я еще раз удвоил количество цифр до $n = 1234567890123456789012345678901234567890. Я получил около 4450 мс для строкового подхода и 9850 мс для целочисленного подхода, резкое изменение которого исключает мой предыдущий постулат.

Краткое описание

Number of digits | String program | Integer program | Difference
---------------------------------------------------------------------------
5                | 800ms          | 550ms           | Integer wins by 250ms
10               | 1330ms         | 1090ms          | Integer wins by 240ms
20               | 2375ms         | 2375ms          | Tie
40               | 4450ms         | 9850ms          | String wins by 4400ms

7 голосов
/ 23 июня 2011

Ответ Стивена впечатляет, но я посмотрел на него пару минут и не смог перевести его в простой ответ, поэтому вот мой.

Для Fixnums

Этобыстрее всего использовать метод цифр, который я приведу ниже.Также довольно быстро (и намного проще) использовать num.to_s.each_char.map(&:to_i).


Для Bignums

Быстрее всего использовать num.to_s.each_char.map(&:to_i).


Решение

Если скорость равна , честно говоря , определяющим фактором того, какой код вы используете (имеется в виду не будьте злым ), тогда этот код является лучшимвыбор для работы.

class Integer
  def digits
    working_int, digits = self, Array.new
    until working_int.zero?
      digits.unshift working_int % 10
      working_int /= 10
    end
    digits
  end
end

class Bignum
  def digits
    to_s.each_char.map(&:to_i)
  end
end

Здесь - подходы, к которым я пришел к выводу.

1 голос
/ 23 июня 2011

Я сделал решение с помощью 'benchmark', используя примеры кода Steven Xu и String # each_byte-version.

require 'benchmark'
MAX = 10_000

#Solution based on /6054244/naskolko-medlennee-stroki-soderzhaschie-chisla-po-sravneniy-s-chislami
class Integer
  def digits
    working_int, digits = self, Array.new
    until working_int.zero?
      digits.unshift working_int % 10
      working_int /= 10
    end
    digits
  end
end

class Bignum
  def digits
    to_s.each_char.map(&:to_i)
  end
end

[
  12345,
  1234567890,
  12345678901234567890,
  1234567890123456789012345678901234567890,
].each{|num|
puts "========="
puts "Benchmark #{num}"
Benchmark.bm do|b|

   b.report("Integer%        ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
            m = n%10
            array.unshift(m)
            n /= 10
        end
        array     
     }
   end

   b.report("Integer% <<     ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
            m = n%10
            array << m
            n /= 10
        end
        array.reverse
     }
   end

   b.report("Integer#divmod  ") do
     MAX.times { 
        array = []
        n = num
        until n == 0
          n, x = *n.divmod(10)
          array.unshift(x)
        end
        array     
     }
   end

   b.report("Integer#divmod<<") do
     MAX.times { 
        array = []
        n = num
        until n == 0
          n, x = *n.divmod(10)
          array << x
        end
        array.reverse
     }
   end

   b.report("String+split//  ") do
     MAX.times { num.to_s.split(//).map {|x| x.to_i} }
   end

   b.report("String#each_byte") do
     MAX.times { num.to_s.each_byte.map{|x| x.chr } }
   end

   b.report("String#each_char") do
     MAX.times { num.to_s.each_char.map{|x| x.to_i } }
   end

   #/6054244/naskolko-medlennee-stroki-soderzhaschie-chisla-po-sravneniy-s-chislami
   b.report("Num#digit       ") do
     MAX.times { num.to_s.each_char.map{|x| x.to_i } }
   end
 end
}

Мои результаты:

Benchmark 12345
      user     system      total        real
Integer%          0.015000   0.000000   0.015000 (  0.015625)
Integer% <<       0.016000   0.000000   0.016000 (  0.015625)
Integer#divmod    0.047000   0.000000   0.047000 (  0.046875)
Integer#divmod<<  0.031000   0.000000   0.031000 (  0.031250)
String+split//    0.109000   0.000000   0.109000 (  0.109375)
String#each_byte  0.047000   0.000000   0.047000 (  0.046875)
String#each_char  0.047000   0.000000   0.047000 (  0.046875)
Num#digit         0.047000   0.000000   0.047000 (  0.046875)
=========
Benchmark 1234567890
      user     system      total        real
Integer%          0.047000   0.000000   0.047000 (  0.046875)
Integer% <<       0.046000   0.000000   0.046000 (  0.046875)
Integer#divmod    0.063000   0.000000   0.063000 (  0.062500)
Integer#divmod<<  0.062000   0.000000   0.062000 (  0.062500)
String+split//    0.188000   0.000000   0.188000 (  0.187500)
String#each_byte  0.063000   0.000000   0.063000 (  0.062500)
String#each_char  0.093000   0.000000   0.093000 (  0.093750)
Num#digit         0.079000   0.000000   0.079000 (  0.078125)
=========
Benchmark 12345678901234567890
      user     system      total        real
Integer%          0.234000   0.000000   0.234000 (  0.234375)
Integer% <<       0.234000   0.000000   0.234000 (  0.234375)
Integer#divmod    0.203000   0.000000   0.203000 (  0.203125)
Integer#divmod<<  0.172000   0.000000   0.172000 (  0.171875)
String+split//    0.266000   0.000000   0.266000 (  0.265625)
String#each_byte  0.125000   0.000000   0.125000 (  0.125000)
String#each_char  0.141000   0.000000   0.141000 (  0.140625)
Num#digit         0.141000   0.000000   0.141000 (  0.140625)
=========
Benchmark 1234567890123456789012345678901234567890
      user     system      total        real
Integer%          0.718000   0.000000   0.718000 (  0.718750)
Integer% <<       0.657000   0.000000   0.657000 (  0.656250)
Integer#divmod    0.562000   0.000000   0.562000 (  0.562500)
Integer#divmod<<  0.485000   0.000000   0.485000 (  0.484375)
String+split//    0.500000   0.000000   0.500000 (  0.500000)
String#each_byte  0.218000   0.000000   0.218000 (  0.218750)
String#each_char  0.282000   0.000000   0.282000 (  0.281250)
Num#digit         0.265000   0.000000   0.265000 (  0.265625)

String # each_byte / each_char быстрее разделения, для меньших чисел целочисленная версия быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...