ruby way之数值计算之一

1 在ruby中表示数值

直接表示:

237 或者+237表示一个正数(如果超出范围,那他就是一个无符数)
-237 表示一个负数

当一个数字太长的话,可以用_将它分割,比如1048576和1_048_576 是一样的。

在ruby中还可以用0b,0,0x分别表示2进制,8进制和16进制的数:
0b10010110      # 二进制
0b1211          # error!
01234           # octal (base 8)
01823           # error!
0xdeadbeef      # hexadecimal (base 16)
0xDEADBEEF      # same
0xdeadpork      # error!


浮点数的表示有两种方式,一种是一般的表示,一种是科学计数法:
3.14            # pi to two digits
-0.628          # -2*pi over 10, to two digits
6.02e23         # Avogadro's number
6.626068e-34    # Planck's constant


在Float类中定义了浮点数所能表示的范围,他们是机器相关的:
Float::MIN          # 2.2250738585072e-308 (on this machine)
Float::MAX          # 1.79769313486232e+308
Float::EPSILON      # 2.22044604925031e-16


2 基本的操作

基本的加减乘除都有,并且还有一个**操作符,这个操作符表示幂。
a = 64**2    # 64的平方4096
b = 64**0.5  # 64的1/2次方 8.0
c = 64**0    # 1
d = 64**-1   # 0.015625


整数的除法,肯定返回整数,如果你想返回一个浮点数,必须其中一个操作数为浮点数:

3 / 3        # 3
5 / 3        # 1
3 / 4        # 0
3.0 / 4      # 0.75
3 / 4.0      # 0.75
3.0 / 4.0    # 0.75


或者你可以使用Float 或者to_f方法,把一个整数转换成浮点数再进行操作:
z = x.to_f / y
z = Float(x) / y


3 浮点数的舍入

如果你想要舍入一个浮点数到整数,你可以使用round方法:

pi = 3.14159
new_pi = pi.round   # 3
temp = -47.6
temp2 = temp.round  # -48

这边他的舍入规则是四舍五入.

有时我们想控制一个浮点数的精度,这时,我们就可以使用sprintf和eval.

pi = 3.1415926535
puts pi6 = eval(sprintf("%8.6f",pi))  # 3.141593  8是代表所表示的最大的位宽,使用eval我们可以去掉它所补得空格
puts pi5 = eval(sprintf("%8.5f",pi))  # 3.14159
puts pi4 = eval(sprintf("%8.4f",pi))  # 3.1416


当然这个看起来非常丑陋.我们来把它压缩成一个方法,然后加到Float类里面:
class Float
  def roundf(places)
    temp = self.to_s.length
    sprintf("%#{temp}.#{places}f",self).to_f
  end
end


4比较浮点数

由于计算机不能精确的表示浮点数时间很令人失望的事情,接下来的代码,如果在完美的世界中应当打印出yes,可是无论我们在那种类型的机器上打印出都是no:
x = 1000001.0/0.003
y = 0.003*x
if y == 1000001.0
  puts "yes"
else
  puts "no"
end


不知道为什么的话,回家好好翻翻计算机系统结构的书。

因此比较浮点数的话,我们只能给定一个精度,如果两个浮点数的差小于这个精度我们就认为他们是相等的:

class Float
  EPSILONS = 1e-6   # 0.000001
  def ==(x)
    (self-x).abs < EPSILONS
  end
end

x = 1000001.0/0.003
y = 0.003*x
puts y
if y == 1000001.0
  puts "yes"
else
  puts "no"
end


我们还可以使用 =~来表示近似相等.

上面这个方法并不是真正的解决方案,真正的解决方案是使用BigDecimal ,稍后我们会介绍到它。
5 格式化一个数字

想要输出格式化,你可以使用Kernel模块的printf方法,这个方法和C语言中的printf是一样的。
x = 345.6789
i = 123
printf("x = %6.2f\n", x)    # x = 345.68
printf("x = %9.2e\n", x)    # x = 3.457e+02
printf("i = %5d\n", i)      # i =   123
printf("i = %05d\n", i)     # i = 00123
printf("i = %-5d\n", i)     # i = 123


如果把想把输出到一个字符串,则可以使用sprintf方法,这个也和C语言中的使用方法是一样的,最终String类还有一个%操作符,他也可以格式化一个字符串:

str = sprintf("%5.1f",x)    # "345.7"
str = "%5.1f" % x            # "345.7"
str = "%6.2f, %05d" % [x,i]  # "345.68, 00123"


6 使用逗号格式化一个字符串

这个我们可以先将它转换为一个字符串,然后将它反转,再用正则表达式来进行操作:

def commas(x)
  str = x.to_s.reverse
  str.gsub!(/([0-9]{3})/,"\\1,")
  str.gsub(/,$/,"").reverse
end

puts commas(123)        # "123"
puts commas(1234)       # "1,234"
puts commas(12345)      # "12,435"
puts commas(123456)     # "123,456"
puts commas(1234567)    # "1,234,567"


7处理大型整数

ruby程序员能够处理计算机所能表示的最大的整数,这里有两个类Fixnum和Bignum,当一个整数大到一定的时候他就会自动从 Fixnum转成Bignum:

num1 = 1000000           # 10**6
num2 = num1*num1         # 10**12
puts num1                # 1000000
puts num1.class          # Fixnum
puts num2                # 1000000000000
puts num2.class          # Bignum


Bignum的计算相比起 Fixnum,更占内存和cpu,因此也就更缓慢一些。

8使用BigDecimal

标准库bigdecimal 能够使我们处理很大的数字,它存储一个浮点数是将它存储为一个数字数组,而不是转换它为一个二进制的表示.它允许任意的精度,虽然会浪费速度.

请看下面的代码:

if (3.2 - 2.0) == 1.2
  puts "equal"
else
  puts "not equal"     # prints "not equal"!
end


我们现在就是要使用bigdecimal,来使他打印出equal.

一个BigDecimal使用一个字符串来实例化。我们能使用BigDecimal.new或者 BigDecimal 来实例化。注意它的to_s方法能够带一个参数,来指定格式化:

require 'bigdecimal'

x = BigDecimal("3.2")
y = BigDecimal("2.0")
z = BigDecimal("1.2")

if (x - y) == z
  puts "equal"         # prints "equal"!
else
  puts "not equal"
end

a = x*y*z
puts a.to_s                 # "0.768E1" (default:  科学计数法)
puts a.to_s("F")            # "7.68"   


我们还可以指定我们想要的表示的数字的有效数字的个数,而precs 方法,则能够接受这个信息,然后返回一个2个值的数组,第一个值是所表示的字节的个数,第二个参数是有效数字的最大个数.
x = BigDecimal("1.234",10)
y = BigDecimal("1.234",15)
x.precs                      # [8, 16]
y.precs                      # [8, 20]


有效数字的最大个数经常比你要求的还要大,那是因为BigDecimal它会自己优化存储。


BigDecimal支持一般的计算符,并且对应的还有计算方法:
a = BigDecimal("1.23456")
b = BigDecimal("2.45678")


puts c  = a+b         # <'0.369134E1',12(20)>
puts c2 = a.add(b,4)  # <'0.3691E1',8(20)>

puts d  = a-b         # <'-0.122222E1',12(20)>
puts d2 = a.sub(b,4)  # <'-0.1222E1',8(20)>

puts e  = a*b         # <'0.3033042316 8E1',16(36)>
puts e2 = a.mult(b,4) # <'0.3033E1',8(36)>

puts f  = a/b         # <'0.5025114173 8372992290 7221E0',24(32)>
puts f2 = a.div(b,4)  # <'0.5025E0',4(16)>


可是这里BigDecimal并不支持%和**操作符。他还定义了E和PI的任意精度的表示,要注意这里,这两个都是方法,而不是实例.

bigdecimal还提供一些子库:

引用
bigdecimal/math
The BigMath module

bigdecimal/jacobian
构建Jacobian 矩阵

bigdecimal/ludcmp
对矩阵执行LU分解

bigdecimal/newton
提供牛顿法结局俺非线性函数的根


9 使用有理数

有理数能表示为两个整数的比率,他只能表示真正的有理数(也就是两个数的商),象PI,E之类的他是不能表示的。
创建一个有理数,我们能使用Rational 方法:

require 'rational'
r = Rational(1,2)   # 1/2 or 0.5
s = Rational(1,3)   # 1/3 or 0.3333...
t = Rational(1,7)   # 1/7 or 0.14...
u = Rational(6,2)   # "same as" 3.0
z = Rational(1,0)   # error!


两个有理数的加减乘除,结果将会是另一个有理数:

r+t          #  Rational(9, 14)
r-t          #  Rational(5, 14)
r*s          #  Rational(1, 6)
r/s          #  Rational(3, 2)


再次回忆我们的浮点数的例子,现在我们可以使用Rational来比较他们:

x = Rational(1000001,1)/Rational(3,1000)
y = Rational(3,1000)*x
if y == 1000001.0
  puts "yes"          # Now we get "yes"!
else
  puts "no"
end


如果我们不想返回一个Rational时可以这样做:

x = Rational(9,16)    #  Rational(9, 16)
Math.sqrt(x)          #  0.75
x**0.5                #  0.75
x**Rational(1,2)      #  0.75


10 矩阵运算

如果你想要处理矩阵,标准库matrix对你来说就很适合。他被分为2个类:Matrix 和Vector。

这里还有一个NArray 库,他不是标准库,可是他速度很快,比标准库更好。可是对于一般的应用标准库也合格了。

我们能够使用Matrix.[] 来创建一个矩阵
require 'matrix'
m = Matrix[[1,2,3],
           [4,5,6],
           [7,8,9]]
         
puts m[2,1] #8


还有一个rows方法,他接受一个数组的数组为参数,顾名思义,也就是接受一个数组,这个数组里面包含很多数组,每个数组都是一行,他还有一个可选的copy参数,默认是copy的:

row1 = [2,3]
row2 = [4,5]
m1 = Matrix.rows([row1,row2])        # copy=true
m2 = Matrix.rows([row1,row2],false)  # don't copy
row1[1] = 99                         # Now change row1
p m1                                 # Matrix[[2, 3], [4, 5]]
p m2                                 # Matrix[[2, 99], [4, 5]]

columns方法和rows方法的使用是一样的,不过就是行变成列了:

p m1 = Matrix.rows([[1,2],[3,4]])  #Matrix[[1, 2], [3, 4]]
p m2 = Matrix.columns([[1,3],[2,4]])  # Matrix[[1, 2], [3, 4]]


矩阵式矩形的,可是Matrix创建的时候不会强迫这么做,这样的话你后面的运算可能会出很多问题:
p m2 = Matrix.columns([[1,3],[2]]) #Matrix[[1, 2], [3, nil]]


我们能够使用identity 方法来定义一个i(或者I和unit也可以,他们只不过是identity的别名):

p im1 = Matrix.identity(3)  # Matrix[[1,0,0],[0,1,0],[0,0,1]]
p im2 = Matrix.I(3)         # same
p im3 = Matrix.unit(3)      # same


更一般的一个方法是scalar,他可以把i中的1变成它的参数:

p sm = Matrix.scalar(3,8)  # Matrix[[8,0,0],[0,8,0],[0,0,8]]


更更一般的方法是diagonal,它的参数的个数是矩阵的大小,而它的参数则依次是矩阵的0,0 1,1 2,2 ... n,n 的值:

p dm = Matrix.diagonal(2,3,7,8)  # Matrix[[2, 0, 0, 0], [0, 3, 0, 0], [0, 0, 7, 0], [0, 0, 0, 8]]


zero方法能够创建一个全0的矩阵:

p zm = Matrix.zero(3)  # Matrix[[0,0,0],[0,0,0],[0,0,0]]


创建一个1Xn或者nX1的矩阵你能使用row_vector 或者column_vector方法:
p a = Matrix.row_vector([2,4,6,8])     # Matrix[[2,4,6,8]]
p b = Matrix.column_vector([6,7,8,9])  # Matrix[[6],[7],[8],[9]]


矩阵中的值可以通过[]来存取,可是这里要注意Matrix是一个不可变对象也就是说它不提供[]=方法:

m = Matrix[[1,2,3],[4,5,6]]
puts m[1,2]    # 6
puts m[1,2]=10 #error


这边要注意数组的下标是从0开始的,而矩阵是从1开始的

我们可以使用row_size和column_size方法来得到一个矩阵的有几列或者有几行,还可以使用:

m1 = Matrix[[1,2,3],[4,5,6],[7,8,9]]
m2 = Matrix[[1,2,3],[4,5,6],[7,8]]
puts m2
puts m1.row_size         # 3
puts m1.column_size      # 3
puts m2.row_size         # 3
puts m2.column_size      # 3     (misleading)
puts m1.square?          # true
puts m2.square?          # true  (incorrect)


这边有个很有意思的就是如果矩阵的行数大于2时,如果你的行列不匹配他不会补上nil,而如果小于2则会补上nil。

row_vectors 和column_vectors 方法,分别能得到一个矩阵的行向量或者列向量,minor 返回一个更小的矩阵,它的参数是4个参数或者2个范围,也就是行和列的最小和最大边界:
m = Matrix[[1,2,3,4],[5,6,7,8],[6,7,8,9]]

puts rows = m.row_vectors       # three Vector objects
puts cols = m.column_vectors    # four Vector objects
puts m2 = m.minor(1,2,1,2)      # Matrix[[6,7,],[7,8]]
puts m3 = m.minor(0..1,1..3)    # Matrix[[[2,3,4],[6,7,8]]



矩阵的操作符,我们提供了addition, subtraction, multiplication, and division这些方法。可是如果这个操作是违规的话就会抛出一个异常,比如你可以试试3*3的一个矩阵乘上一个4*4的矩阵.

向量也就是矩阵的组成元素,他有一个elements方法来取得他的值,他也有一个可选的参数copy,默认是copy:
arr = [2,3,4,5]
puts v1 = Vector[*arr]                # Vector[2,3,4,5]
puts v2 = Vector.elements(arr)        # Vector[2,3,4,5]
puts v3 = Vector.elements(arr,false)  # Vector[2,3,4,5]
arr[2] = 7                       # v3 is now Vector[2,3,7,5]
puts v3          #[2,3,7,5] 
puts v2       #Vector[2, 3, 4, 5]


covector方法转变一个向量为一个矩阵:

v = Vector[2,3,4]
puts m = v.covector    # Matrix[[2,3,4]]


向量的加减乘除操作符也是支持的,不过要注意,向量的乘积会是一个矩阵:
v1 = Vector[2,3,4]
v2 = Vector[4,5,6]
v3 = v1 + v2         # Vector[6,8,10]
v4 = v1*v2.covector  # Matrix[[8,10,12],[12,15,18],[16,20,24]]
v5 = v1*5            # Vector[10,15,20]


这里还有一个inner_product方法:

v1 = Vector[2,3,4]
v2 = Vector[4,5,6]
puts x  = v1.inner_product(v2)   # 47=2*4+3*5+4*6












你可能感兴趣的:(正则表达式,F#,Ruby,D语言)