ruby way之数值计算之一

1 在ruby中表示数值

直接表示:

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

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

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

浮点数的表示有两种方式,一种是一般的表示,一种是科学计数法:
[code]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[/code]

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

2 基本的操作

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

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

[code]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[/code]

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

3 浮点数的舍入

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

[code]pi = 3.14159
new_pi = pi.round # 3
temp = -47.6
temp2 = temp.round # -48[/code]
这边他的舍入规则是四舍五入.

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

[code]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[/code]

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

4比较浮点数

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

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

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

[code]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[/code]

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

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

想要输出格式化,你可以使用Kernel模块的printf方法,这个方法和C语言中的printf是一样的。
[code]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[/code]

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

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

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

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

[code]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"[/code]

7处理大型整数

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

[code]num1 = 1000000 # 10**6
num2 = num1*num1 # 10**12
puts num1 # 1000000
puts num1.class # Fixnum
puts num2 # 1000000000000
puts num2.class # Bignum[/code]

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

8使用BigDecimal

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

请看下面的代码:

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

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

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

[code]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" [/code]

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

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


BigDecimal支持一般的计算符,并且对应的还有计算方法:
[code]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)>[/code]

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

bigdecimal还提供一些子库:

[quote]bigdecimal/math
The BigMath module

bigdecimal/jacobian
构建Jacobian 矩阵

bigdecimal/ludcmp
对矩阵执行LU分解

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

9 使用有理数

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

[code]
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![/code]

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

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

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

[code]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[/code]

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

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

10 矩阵运算

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

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

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

puts m[2,1] #8
[/code]

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

[code]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]][/code]
columns方法和rows方法的使用是一样的,不过就是行变成列了:

[code]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]][/code]

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

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

[code]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[/code]

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

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

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

[code]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]][/code]

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

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

创建一个1Xn或者nX1的矩阵你能使用row_vector 或者column_vector方法:
[code]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]][/code]

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

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

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

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

[code]
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)[/code]

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

row_vectors 和column_vectors 方法,分别能得到一个矩阵的行向量或者列向量,minor 返回一个更小的矩阵,它的参数是4个参数或者2个范围,也就是行和列的最小和最大边界:
[code]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]][/code]


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

向量也就是矩阵的组成元素,他有一个elements方法来取得他的值,他也有一个可选的参数copy,默认是copy:
[code]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]
[/code]

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

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

向量的加减乘除操作符也是支持的,不过要注意,向量的乘积会是一个矩阵:
[code]
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][/code]

这里还有一个inner_product方法:

[code]v1 = Vector[2,3,4]
v2 = Vector[4,5,6]
puts x = v1.inner_product(v2) # 47=2*4+3*5+4*6[/code]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值