Ruby 中的数字运算(需要优化) [英] Number crunching in Ruby (optimisation needed)

查看:47
本文介绍了Ruby 中的数字运算(需要优化)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Ruby 可能不是这方面的最佳语言,但我对在我的终端中使用它感到很舒服,所以这就是我要使用的.

Ruby may not be the optimal language for this but I'm sort of comfortable working with this in my terminal so that's what I'm going with.

我需要处理从 1 到 666666 的数字,所以我将所有包含 6 但不包含 7、8 或 9 的数字标记出来.第一个数字将是 6,下一个数字将是16,然后是 26 等等.然后我需要它像这样打印 (6=6) (16=6) (26=6) 并且当我有像 6066 我需要像 (60 THRU 66=6)(SPSS 语法)一样打印它.

I need to process the numbers from 1 to 666666 so I pin out all the numbers that contain 6 but doesn't contain 7, 8 or 9. The first number will be 6, the next 16, then 26 and so forth. Then I needed it printed like this (6=6) (16=6) (26=6) and when I have ranges like 60 to 66 I need it printed like (60 THRU 66=6) (SPSS syntax).

我有这段代码,它可以工作,但它既不美观也不高效,那么我该如何优化它?

I have this code and it works but it's neither beautiful nor very efficient so how could I optimize it?

(后面可能是傻代码)

class Array
  def to_ranges
    array = self.compact.uniq.sort
    ranges = []
    if !array.empty?
      # Initialize the left and right endpoints of the range
      left, right = array.first, nil
      array.each do |obj|
        # If the right endpoint is set and obj is not equal to right's successor 
        # then we need to create a range.
        if right && obj != right.succ
          ranges << Range.new(left,right)
          left = obj
        end
        right = obj
      end
      ranges << Range.new(left,right) unless left == right
    end
    ranges
  end
end

write = ""
numbers = (1..666666).to_a

# split each number in an array containing it's ciphers
numbers = numbers.map { |i| i.to_s.split(//) }

# delete the arrays that doesn't contain 6 and the ones that contains 6 but also 8, 7 and 9
numbers = numbers.delete_if { |i| !i.include?('6') }
numbers = numbers.delete_if { |i| i.include?('7') }
numbers = numbers.delete_if { |i| i.include?('8') }
numbers = numbers.delete_if { |i| i.include?('9') }

# join the ciphers back into the original numbers
numbers = numbers.map { |i| i.join }

numbers = numbers.map { |i| i = Integer(i) }

# rangify consecutive numbers
numbers = numbers.to_ranges

# edit the ranges that go from 1..1 into just 1
numbers = numbers.map do |i|
  if i.first == i.last
    i = i.first
  else
    i = i
  end
end

# string stuff
numbers = numbers.map { |i| i.to_s.gsub(".."," thru ") }

numbers = numbers.map { |i| "(" + i.to_s + "=6)"}

numbers.each { |i| write << " " + i }

File.open('numbers.txt','w') { |f| f.write(write) }

正如我所说,它适用于数以百万计的数字 - 但我想要一些关于如何使数据更漂亮、更高效的建议.

As I said it works for numbers even in the millions - but I'd like some advice on how to make prettier and more efficient.

推荐答案

我删除了我之前对 parlez-vous-ruby? 的尝试并弥补了这一点.我知道有 x3ro 优秀示例的优化版本.

I deleted my earlier attempt to parlez-vous-ruby? and made up for that. I know have an optimized version of x3ro's excellent example.

$,="\n"
puts ["(0=6)", "(6=6)", *(1.."66666".to_i(7)).collect {|i| i.to_s 7}.collect do |s|
    s.include?('6')? "(#{s}0 THRU #{s}6=6)" : "(#{s}6=6)"
end ]

与 x3ro 的版本相比

Compared to x3ro's version

它使用了我所有的优化想法

It uses all my ideas for optimization

  • 基于 7 位模数的代数(所以基数为 7 数)
  • 生成最后一位智能":这是压缩范围的原因

那么……时间是什么时候?这是用 8 位数字进行测试(到 66666666,或 823544 行输出):

So... what are the timings? This was testing with 8 digits (to 66666666, or 823544 lines of output):

$ time ./x3ro.rb >  /dev/null

real    8m37.749s
user    8m36.700s
sys 0m0.976s

$ time ./my.rb >  /dev/null

real    0m2.535s
user    0m2.460s
sys 0m0.072s

即使性能实际上很好,它甚至不接近C 优化版 我之前发帖说:由于 OutOfMemory,我无法将 my.rb 运行到 6666666666 (6x10).跑到9位数时,这是比较结果:

Even though the performance is actually good, it isn't even close to the C optimized version I posted before: I couldn't run my.rb to 6666666666 (6x10) because of OutOfMemory. When running to 9 digits, this is the comparative result:

sehe@meerkat:/tmp$ time ./my.rb >  /dev/null

real    0m21.764s
user    0m21.289s
sys 0m0.476s

sehe@meerkat:/tmp$ time ./t2 > /dev/null

real    0m1.424s
user    0m1.408s
sys 0m0.012s

C 版本仍然快 15 倍......考虑到它在裸机上运行,​​这是公平的.

The C version is still some 15x faster... which is only fair considering that it runs on the bare metal.

希望你喜欢它,如果只是为了学习 Ruby,我可以请你投票吗:)

Hope you enjoyed it, and can I please have your votes if only for learning Ruby for the purpose :)

(你能说我很自豪吗?这是我第一次接触 ruby​​;我在 2 小时前开始了 ruby​​ koans...)

编辑@johndouthat:

非常好!base7 的使用非常聪明,这对于您的第一次 ruby​​ 试用来说非常棒:)

Very nice! The use of base7 is very clever and this a great job for your first ruby trial :)

这里对您的代码段稍作修改,可以让您测试 10 多个数字而不会出现 OutOfMemory 错误:

Here's a slight modification of your snippet that will let you test 10+ digits without getting an OutOfMemory error:

puts ["(0=6)", "(6=6)"]
(1.."66666666".to_i(7)).each do |i| 
  s = i.to_s(7)
  puts s.include?('6') ? "(#{s}0 THRU #{s}6=6)" : "(#{s}6=6)"
end

# before:
real    0m26.714s
user    0m23.368s
sys 0m2.865s
# after
real    0m15.894s
user    0m13.258s
sys 0m1.724s

这篇关于Ruby 中的数字运算(需要优化)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆