convert number to digits

There are a plenty of ways to convert integer to array of digits. You may want to do it if you write #algorithm to find factorions. Check out several solutions and benchmarks, I expect you to post your own solution (I will add them to benchmark asap).

The first three methods are based on operating with divmod function, which is applied to its result again and again until it is less than 10. The other approach is to convert number to string, split it and than convert chars to integers again. Let's compare several implementations:

require 'benchmark'
require 'benchmark/ips'

def recursive_divmod_digits(d, digits = [])
  return [d] if d < 10
  mod = d.divmod(10)

  if mod.first < 10
    return mod + digits
  else
    recursive_divmod_digits(mod.first, digits.insert(0, mod.last))
  end
end

def loop_divmod_digits(d)
  base, last_digit = d.divmod(10)
  digits = [last_digit]

  while base > 9 do
    base, last_digit = base.divmod(10)
    digits = digits.insert(0, last_digit)
  end

  digits = digits.insert(0, base)

  digits
end

def loop_divmod_digits_with_reverse(base)
  digits = []

  while base > 9 do
    base, last_digit = base.divmod(10)
    digits << last_digit
  end

  digits << base

  digits.reverse
end

def mikdie_digits(base)
  digits = []

  while base != 0 do
    base, last_digit = base.divmod(10)
    digits << last_digit
  end

  digits.reverse
end

def evserykh_digits(d)
  d.to_s.split(//).map(&:to_i)
end

def chars_to_i_digits(d)
  d.to_s.chars.to_a.map(&:to_i)
end

def chars_to_i_without_to_a_digits(d)
  d.to_s.chars.map(&:to_i)
end

def buddhamagnet_digits(d)
  d.to_s.split('').map(&:to_i)
end

def burns_digits(d)
  digits = []
  while d > 0
    digits << d % 10
    d /= 10
  end
  digits.reverse!
end

def mikecmpbll_digits(int)
  arr = []
  loop do
    arr << int and break if int == 0 || (ord = Math.log(int,10).to_i) == 0
    arr << (f = int/(m = 10**ord))
    int -= f*m
  end
  return arr
end

def mikecmpbll_digits_second(int)
  return [0] if int == 0
  arr = []
  ord = int.to_s.length-1
  while true do
    arr << int and break if ord == 0
    arr << (f = int/(m = 10**ord))
    int -= f*m
    ord -= 1
  end
  return arr
end

Benchmark.ips do |r|
  N = 1234567

  r.report("recursive_divmod_digits") do
    recursive_divmod_digits(N)
  end

  r.report("loop_divmod_digits") do
    loop_divmod_digits(N)
  end

  r.report("chars_to_i_digits") do
    chars_to_i_digits(N)
  end

  r.report("loop_divmod_digits_with_reverse") do
    loop_divmod_digits_with_reverse(N)
  end

  r.report("chars_to_i_without_to_a_digits") do
    chars_to_i_without_to_a_digits(N)
  end

  r.report("mikdie_digits") do
    mikdie_digits(N)
  end

  r.report("evserykh_digits") do
    evserykh_digits(N)
  end

  r.report("buddhamagnet_digits") do
    buddhamagnet_digits(N)
  end

  r.report("burns_digits") do
    burns_digits(N)
  end

  r.report("mikecmpbll_digits") do
    mikecmpbll_digits(N)
  end

  r.report("mikecmpbll_digits_second") do
    mikecmpbll_digits_second(N)
  end
end

# Calculating -------------------------------------
# recursive_divmod_digits
#                          17207 i/100ms
#   loop_divmod_digits     21417 i/100ms
#    chars_to_i_digits     16103 i/100ms
# loop_divmod_digits_with_reverse
#                          25491 i/100ms
# chars_to_i_without_to_a_digits
#                          15990 i/100ms
#        mikdie_digits     23158 i/100ms
#      evserykh_digits      8495 i/100ms
#  buddhamagnet_digits      7996 i/100ms
#         burns_digits     38005 i/100ms
#    mikecmpbll_digits      8135 i/100ms
# mikecmpbll_digits_second
#                          25112 i/100ms
# -------------------------------------------------
# recursive_divmod_digits
#                        205734.7 (±7.2%) i/s -    1032420 in   5.044263s
#   loop_divmod_digits   309315.2 (±12.3%) i/s -    1520607 in   5.001965s
#    chars_to_i_digits   163030.0 (±26.2%) i/s -     756841 in   5.054871s
# loop_divmod_digits_with_reverse
#                        365695.1 (±10.2%) i/s -    1809861 in   5.004672s
# chars_to_i_without_to_a_digits
#                        217060.6 (±11.4%) i/s -    1087320 in   5.074630s
#        mikdie_digits   402766.9 (±9.8%) i/s -    1991588 in   4.999393s
#      evserykh_digits   103643.8 (±7.3%) i/s -     518195 in   5.028034s
#  buddhamagnet_digits    92330.2 (±14.5%) i/s -     447776 in   5.003551s
#         burns_digits   800352.8 (±16.5%) i/s -    3914515 in   5.012195s
#    mikecmpbll_digits    99875.2 (±5.4%) i/s -     504370 in   5.064975s
# mikecmpbll_digits_second
#                        439225.3 (±16.0%) i/s -    2159632 in   5.043572s

As you can see loop_divmod_digits_with_reverse wins :smiley:

P.S. Post your approaches to comments, they will be benchmarked asap.

UPDATE

  1. @Mik-die refactoring of loop_divmod_digits_with_reverse

  2. Added @Mik-die and @evserykh solutions. mikdie_digits is the leader.

  3. Added chars_to_i_without_to_a_digits, seems a lil bit faster. Thx to @Mik-die for hint.

  4. Added @buddhamagnet solution with split('').

  5. Added @burns solution, currently the fastest.

  6. Added @mikecmpbll solution with Math and stuff.

  7. Added @mikecmpbll second solution.

#ruby #digits