Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby에서 분수와 유리로 작업하기

고백할게 있습니다. 저는 부동 소수점 숫자를 싫어합니다. 물론, 컴퓨터라면 유용하지만, 사람이라면 다음과 같은 상황에서 왼쪽 머리를 긁적입니다.

129.95 * 100
# => 12994.999999999998

이것은 수학적 조화에 직면할 뿐만 아니라 나쁜 UX입니다.

레시피에서 밀가루 0.37211927843컵을 측정하라고 하면 작성자가 바보인지 스스로 웃으며 컵의 3분의 1을 측정하기 시작했을 것입니다.

대부분의 사람들은 임의의 십진수에 대해 생각할 수 있는 것보다 훨씬 더 쉽게 분수에 대해 생각할 수 있습니다. 따라서 앱에서 사람들에게 숫자를 전달하려는 경우 숫자를 분수로 표현하는 방법을 찾는 것이 좋습니다.

Ruby의 Rational class는 유리수 작업을 위한 훌륭한 도구입니다. 그것은 당신에게 합리적인 수학을 할 수 있는 능력을 제공할 뿐만 아니라, 또한 당신이 근사한 부동 소수점 숫자를 근사하는 간단한 분수를 찾을 수 있게 해줍니다. 살펴보겠습니다!

합리성이란 무엇입니까?

우리의 목적을 위해 "유리수"는 "분수"를 말하는 멋진 방법입니다. 분자와 분모의 두 부분으로 구성됩니다.

1/2 # Numerator is 1. Denominator is 2. 
5   # Numerator is 5. Denominator is 1.

Ruby에서 유리수는 정수 및 부동 소수점 숫자와 마찬가지로 고유한 데이터 유형을 갖습니다. 새로운 합리성을 만드는 몇 가지 방법이 있습니다.

3/2r              # This syntax was introduced in Ruby 2.1
1.5.to_r          # Floats can be converted to rationals via `to_r`
"3/2".to_r        # ...so can strings
Rational('3/2')   # This is how we had to do things in the olden days
Rational(3, 2)    # ...see how hard life was?

간단한 수학

두 개의 유리수를 더하거나 빼거나 곱하거나 나누면 결과도 유리합니다.

2/3r + 1/3r
# => (1/1)
2/3r - 1/3r
# => (1/3)
2/3r * 1/3r
# => (2/9)
(2/3r) / (1/3r) # We need parens here to avoid confusing the interpreter
# => (2/1)

다른 모든 수학 연산자도 예상대로 작동합니다. ** , > , < 등.

일반적인 규칙은 결과가 분수가 되려면 두 입력이 모두 분수여야 한다는 것입니다. 내가 찾을 수있는 한 가지 예외는 정수입니다. 모든 정수는 합리적이므로 Ruby는 똑똑한 작업을 수행하고 모든 사람이 합리적인 출력을 가정한다고 가정합니다.

2/3r + 2
# => (8/3)

근사치

유리수에 대한 가장 유용한 점 중 하나는 우리가 머리로 근사하고 쉽게 계산할 수 있다는 것입니다. 이것을 이용하려면 분수를 단순하게 유지해야 합니다. 3/2 3320774221237909/2251799813685248 대신 .

다행히 Ruby는 정확하지만 못생긴 숫자를 근사하지만 예쁜 숫자로 쉽게 변환할 수 있는 방법을 제공합니다. 저는 rationalize에 대해 이야기하고 있습니다. 방법.

다음과 같습니다.

# Precise but ugly
(1.47472).to_r
=> (3320774221237909/2251799813685248)

# Less precise, but pretty
(1.47472).to_r.rationalize(0.05)
=> (3/2)

합리화 방법에는 하나의 인수가 있습니다. 단순성을 위해 기꺼이 교환하려는 정밀도의 정도인 허용 오차를 지정합니다.

이 방법은 허용 오차 내에서 분모가 가장 낮은 숫자를 찾습니다. 제가 의미하는 바는 다음과 같습니다.

# What's the number with the lowest denominator between 5/10 and 7/10?
(6/10r).rationalize(1/10r)
# => (1/2)

# What's the number with the lowest denominator between 11/20 and 13/20?
(6/10r).rationalize(1/20r)
=> (3/5)

# ..and between 1/10 and 11/10?
(6/10r).rationalize(1/2r)
=> (1/1)

크루더 근사치

분수에 해당하는 정수 또는 부동 소수점 수를 찾기만 하면 여러 가지 옵션이 있습니다.

# Return the nearest integer. 
(6/10r).round
# => 1

# Round down
(12/10r).to_i 
# => 1

합리성의 한계

유리수와 Ruby로 작업할 때 알아야 할 몇 가지 제한 사항이 있습니다. 물론 0으로 나눌 수는 없습니다.

4/0r
ZeroDivisionError: divided by 0

무리한 수를 합리적으로 취급하려고 하면 이상한 행동을 하게 될 것입니다.

# Umm, isn't the square root of 2 irrational?
Rational(Math.sqrt(2))
# => (6369051672525773/4503599627370496)

# And I'm pretty sure PI is irrational as well.
Rational(Math::PI)
# => (884279719003555/281474976710656)

우리는 Ruby에게 무리수를 처리하도록 요청하는 것이 합리적이라고 생각하면 일종의 예외가 발생할 것이라고 기대할 수 있습니다. 그러나 불행히도 Ruby는 이것을 할 만큼 똑똑하지 않은 것 같습니다. 대신, 이러한 무리수에 대한 부동 소수점 근사값을 유리수로 변환합니다. 큰 문제는 아니지만 주의해야 할 사항입니다.