Ruby yields results in fraction for matrix inverse operation but not for matrix multiplication operation. E.g., below code:
require 'matrix'
(Matrix[ [-1/2] ] * Matrix[ [1/2]])
yields Matrix[[0]] instead of Matrix[[-1/4]]. Why this behavior?
Your problem has been identified, but your real question has not been answered, namely, when multiplying matrix objects, when is an element of the product an integer, a rational number or a float?
If a and b are matrix objects, each element of a*b will be:
an integer if all elements of a and b used in its calculation are integers;
a rational number if, among the elements of a and b used in its calculation there is at least one rational number and the remainder are integers or rationals; and
a float if at least one of the elements of a and b used in its calculation is a float.
I will give a few examples, but first let's consider how Ruby expresses rational numbers. A rational number is a number that can be expressed as the ratio of two integers. For example, 1.5 is a rational number since it can be expressed 3/2. We can't write it that way in Ruby, however, since 3/2 will be replaced by 1, the result of integer division. Instead, we create an instance of the class Rational:
r = Rational(3,2)
#=> (3/2)
and use that in the calculations. (Note the parentheses in the return value.) We can extract its numerator and denominator, or convert it to an integer (rounding down or up) or a float:
r.numerator
#=> 3
r.denominator
#=> 2
r.to_i
#=> 1
r.ceil
#=> 2
r.to_f
#=> 1.5
Now let's look at some examples.
require 'matrix'
Matrix[[Rational(-1,2)]] * Matrix[[Rational(1,2)]]
#=> Matrix[[(-1/4)]]
Matrix[[-1]] * Matrix[[Rational(1,2)]]
# => Matrix[[(-1/2)]]
Matrix[[-0.5]] * Matrix[[Rational(1,2)]]
#=> Matrix[[-0.25]]
Matrix[[Rational(-1,2), Rational(1,2)]].transpose * Matrix[[Rational(1,2), 0.5]]
#=> Matrix[[(-1/4), -0.25], [(1/4), 0.25]]
Now let's consider the inverse of a matrix:
Matrix[[Rational(-1,2), 1],[2, Rational(2,3)]].inverse
#=> Matrix[[(-2/7), (3/7)], [(6/7), (3/14)]]
Matrix[[Rational(-1,2), 1.0],[2, Rational(2,3)]].inverse
#=> Matrix[[-0.2857142857142857, 0.4285714285714286],
# [0.8571428571428571, 0.21428571428571427]]
In calculating the inverse of a matrix with n rows and columns there are n "pivoting" steps. If, as in my latter example, the matrix contains a mix of integers, rationals and floats, when each pivot operation is performed:
each integer will be converted to a float if at least one float is used in its calculation, else it will be converted to a rational if at least one rational is used in its calculation; else it will remain an integer;
each rational will be converted to a float if there is at least one float used in its calculation, else it will remain a rational; and
each float will remain a float.
As "once a float always a float", it won't be long before all elements of the computed matrix are floats. (I believe it can be proven that the inverse will contain all floats if the original matrix contains at least one float.)
you need to choose correct type of data. Int divided by int is int.
1/2 -> 0
1.0/2 -> 0.5
1/2.0 -> 0.5
0.5 -> 0.5
1/2r -> (1/2)
(1/2r).to_f -> 0.5
Matrix[ [-1/2r] ] * Matrix[ [1/2r]]
so basically Matrix[ [-1/2] ] * Matrix[ [1/2] ] is the same as Matrix[ [-1] ] * Matrix[ [0] ]
There is no fractional result for integer division. For example, 5 divided by 3 is 1 with remainder 2. In your code, -1/2 is -1, and 1/2 is 0. And -1 times 0 is 0.
Related
When I create a new opal, I want to randomly assign it one of many possible features. However, I want some qualities to be more common than others. I have a hash with possible features and their relative probability (out of a total of 1).
How do I choose a feature at random, but weighted according to the probability?
'possible_features':
{
'white_pin_fire_green': '0.00138',
'white_pin_fire_blue': '0.00138',
'white_pin_fire_yellow': '0.00144',
'white_pin_fire_purple': '0.00144',
'white_pin_fire_pink': '0.00036',
'white_straw_green': '0.01196',
'white_straw_blue': '0.01196',
'white_straw_yellow': '0.01248',
'white_straw_purple': '0.01248',
'white_straw_pink': '0.00312',
'white_ribbon_green': '0.01196',
'white_ribbon_blue': '0.01196',
'white_ribbon_yellow': '0.01248',
'white_ribbon_purple': '0.01248',
'white_ribbon_pink': '0.00312',
'white_harlequin_green': '0.0069',
'white_harlequin_blue': '0.0069',
'white_harlequin_yellow': '0.0072',
'white_harlequin_purple': '0.0072',
'white_harlequin_pink': '0.0018',
'white_no_fire': '0.06',
'black_pin_fire_green': '0.00552',
'black_pin_fire_blue': '0.00552',
'black_pin_fire_yellow': '0.00576',
'black_pin_fire_purple': '0.00576',
'black_pin_fire_pink': '0.00144',
'black_straw_green': '0.04784',
'black_straw_blue': '0.04784',
'black_straw_yellow': '0.04992',
'black_straw_purple': '0.04992',
'black_straw_pink': '0.01248',
'black_ribbon_green': '0.04784',
'black_ribbon_blue': '0.04784',
'black_ribbon_yellow': '0.04992',
'black_ribbon_purple': '0.04992',
'black_ribbon_pink': '0.01248',
'black_harlequin_green': '0.0276',
'black_harlequin_blue': '0.0276',
'black_harlequin_yellow': '0.0288',
'black_harlequin_purple': '0.0288',
'black_harlequin_pink': '0.0072',
'black_no_fire': '0.24'
}
For example, if I randomly generate 100 opals, I'd like for approximately 24 of them to have the "black_no_fire" feature.
Thank you for any help!
If I can assume that the hash values do indeed add up to exactly 1.0, then the solution is little simpler. (Otherwise, this approach would still work, but requires a little extra effort to first sum all the values - and use them as a weighting, but not a direct probability.)
First, let's choose a random value between 0 and 1, to represent a "fair selection". You may wish to use SecureRandom.random_number in your implementation.
Then, I loop through the possibilities, seeing when the cumulative sum reaches the chosen value.
possible_features = {
white_pin_fire_green: "0.00138",
white_pin_fire_blue: "0.00138",
# ...
}
r = rand
possible_features.find { |choice, probability| (r -= probability.to_f) <= 0 }.first
This effectively treats each possibility as covering a range: 0 .. 0.00138, 0.00138 .. 0.00276, 0.00276 .. 0.00420, ..., 0.76 .. 1.
Since the original random value (r) is was chosen from an even distribution, its value will lie within one of those ranges with the desired weighted probability.
Suppose your hash were as follows.
pdf = {
white_pin_fire_green: 0.21,
white_pin_fire_blue: 0.25,
white_pin_fire_yellow: 0.23,
white_pin_fire_purple: 0.16,
white_pin_fire_pink: 0.15
}
pdf.values.sum
#=> 1.0
I've made the values floats rather than strings merely to avoid the need for a boring conversion. Note that the keys, which are symbols, do not require single quotes here.
We can assume that all of the values in pdf are positive, as any that are zero can be removed.
Let's first create a method that converts pdf (probability density function) to cdf (cumulative probability distribution).
def pdf_to_cdf(pdf)
cum = 0.0
pdf.each_with_object({}) do |(k,v),h|
cum += v
h[cum] = k
end
end
cdf = pdf_to_cdf(pdf)
#=> {0.21=>:white_pin_fire_green,
# 0.45999999999999996=>:white_pin_fire_blue,
# 0.69=>:white_pin_fire_yellow,
# 0.85=>:white_pin_fire_purple,
# 1.0=>:white_pin_fire_pink}
Yes, I've inverted the cdf by flipping the keys and values. That's not a problem, since all pdf values are positive, and it's more convenient this way, for reasons to be explained.
For convenience let's now create an array of cdf's keys.
cdf_keys = cdf.keys
#=> [0.21, 0.46, 0.69, 0.85, 1.0]
We sample a single probability-weighted value by generating a (pseudo-) random number p between 0.0 and 1.0 (e.g., p = rand #=> 0.793292984248818) and then determine the smallest index i for which
cdf_keys[i] >= p
Suppose p = 0.65. then
cum_prob = cdf_keys.find { |cum_prob| cum_prob >= 0.65 }
#=> 0.69
Note that, because cdf_keys is an increasing sequence the operation
cum_prob = cdf_keys.find { |cum_prob| cum_prob >= rand }
could be sped up by using Array#bsearch.
So we select
selection = cdf[cum_prob]
#=> :white_pin_fire_yellow
Note that the probability that rand will be between 0.46 and 0.69 equals 0.69 - 0.46 = 0.23, which, by construction, is the desired probability of selecting :white_pin_fire_yellow.
If we wish to sample additional values "with replacement", we simply generate additional random numbers between zero and one and repeat the above calculations.
If we wish to sample additional values "without replacement" (no repeated selections), we must first remove the element just drawn from the pdf. First, however, let's note the probability of selection:
selection_prob = pdf[selection]
#=> 0.23
Now delete selection from pdf.
pdf.delete(:white_pin_fire_yellow)
pdf
#=> {:white_pin_fire_green=>0.21,
# :white_pin_fire_blue=>0.25,
# :white_pin_fire_purple=>0.16,
# :white_pin_fire_pink=>0.15}
As pdf.values.sum #=> 0.77 we must normalize the values so they sum to 1.0. To do that we don't actually have to sum the values as that sum equals
adj = 1.0 - selection_prob
#=> 1.0 - 0.23 => 0.77
Now normalize the new pdf:
pdf.each_key { |k| pdf[k] = pdf[k]/adj }
#=> {:white_pin_fire_green=>0.2727272727272727,
# :white_pin_fire_blue=>0.3246753246753247,
# :white_pin_fire_purple=>0.20779220779220778,
# :white_pin_fire_pink=>0.1948051948051948}
pdf.values.sum
#=> 1.0
We now repeat the steps described above when selecting the first element at random (construct cdf, generate a random number between zero and one, and so on).
I'm solving some Project Euler problems using Ruby, and specifically here I'm talking about problem 25 (What is the index of the first term in the Fibonacci sequence to contain 1000 digits?).
At first, I was using Ruby 2.2.3 and I coded the problem as such:
number = 3
a = 1
b = 2
while b.to_s.length < 1000
a, b = b, a + b
number += 1
end
puts number
But then I found out that version 2.4.2 has a method called digits which is exactly what I needed. I transformed to code to:
while b.digits.length < 1000
And when I compared the two methods, digits was much slower.
Time
./025/problem025.rb 0.13s user 0.02s system 80% cpu 0.190 total
./025/problem025.rb 2.19s user 0.03s system 97% cpu 2.275 total
Does anyone have an idea why?
Ruby's digits
... is implemented in rb_int_digits.
Which for non-tiny numbers (i.e., most of your numbers) uses rb_int_digits_bigbase.
Which extracts digit after digit naively with division/modulo by base.
So it should take quadratic time (at least with a small base such as 10).
Ruby's to_s
... is implemented in int_to_s.
Which uses rb_int2str.
Which for non-tiny numbers uses rb_big2str.
Which uses rb_big2str1.
Which might use big2str_gmp if available (which sounds/looks like it uses the fast GMP library) or ...
... uses big2str_generic.
Which uses big2str_karatsuba (sweet, I recognize that name!).
Which looks like it has something to do with ...
... Karatsuba's algorithm, which is a fast multiplication algorithm. If you multiply two n-digit numbers the naive way you learned in school, you take n2 single-digit products. Karatsuba on the other hand only needs about n1.585, which is quite a lot better. And I didn't read into this further, but I suspect what Ruby does here is also this efficient. Eric Lippert's answer with a base conversion algorithm uses Karatsuba multiplication and says "this [base conversion] algorithm is utterly dominated by the cost of the multiplication".
Comparing quadratic to n1.585 over the number lengths from 1 digit to 1000 digits gives factor 15:
(1..1000).sum { |i| i**2 } / (1..1000).sum { |i| i**1.585 }
=> 15.150583254950678
Which is roughly the factor you observed as well. Of course that's a rather naive comparison, but, well, why not.
GMP by the way apparently uses/used a "near O(n * log(n)) FFT-based multiplication algorithm".
Thanks to #Drenmi's answer for motivating me to dig into the source after all. I hope I did this right, no guarantees, I'm a Ruby beginner. But that's why I left all the links there for you to check for yourself :-P
Integer#digits doesn't just "split" the number. From the documentation:
Returns the array including the digits extracted by place-value
notation with radix base of int.
This extraction is done even if a base argument is omitted. The relevant source:
# ruby/numeric.c:4809
while (!FIXNUM_P(num) || FIX2LONG(num) > 0) {
VALUE qr = rb_int_divmod(num, base);
rb_ary_push(digits, RARRAY_AREF(qr, 1));
num = RARRAY_AREF(qr, 0);
}
As you can see, this process includes repeated modulo arithmetics, which likely accounts for the additional runtime.
Many ruby methods create objects (strins, arrays, etc.)
In ruby, object creation in ruby is "expensive".
For instance to_s creates a string and digits creates an array every time the while condition is evaluated.
If you want to optimize your example, you can do the following:
# create the smallest possible 1000 digits number
max = 10**999
number = 3
a = 1
b = 2
# do not create objects in while condition
while b < max
a, b = b, a + b
number += 1
end
puts number
I have not answered your question, but wish to suggest an improved algorithm for the problem you have addressed. For a given number of decimal digits, n, I have implemented the following algorithm.
estimate the number f of Fibonacci numbers ("FNs") that have n or fewer decimal digits.
compute the fth and (f-1)st FNs, and the number of digits m in the fth FN.
if m >= n back down from down from the (f-1)st FN until the (f-1)st FN has fewer than n decimal digits, at which time the fth FN is the smallest FN to have n decimal digits.
if m < n increase the fth FN until the it has n decimal digits, at which time it is the smallest FN to have n decimal digits.
The key is to compute a close estimate f in the first step.
Code
AVG_FNs_PER_DIGIT = 4.784971966781667
def first_fibonacci_with_n_digits(n)
return [1, 1] if n == 1
idx = (n * AVG_FNs_PER_DIGIT).round
fn, prev_fn = fib(idx)
fn.to_s.size >= n ? fib_down(n, fn, prev_fn, idx) : fib_up(n, fn, prev_fn, idx)
end
def fib(idx)
a = 1
b = 2
(idx - 2).times {a, b = b, a + b }
[b, a]
end
def fib_up(n, b, a, idx)
loop do
a, b = b, a + b
idx += 1
break [idx, b] if b.to_s.size == n
end
end
def fib_down(n, b, a, idx)
loop do
a, b = b - a, a
break [idx, b] if a.to_s.size == n - 1
idx -= 1
end
end
Benchmarks
In computing each Fibonacci number two operations are typically performed:
compute the number of digits in the last-computed Fibonacci number and if that number is equal to the target number of digits, terminate (for reasons made clear in the Explanation section below, it cannot be larger than the target number); else
compute the next number in the Fibonacci sequence.
By contrast, the method I have proposed performs the first step a relatively small number of times.
How important is the first step relative to the second and how does the use of n.digits.size compare with that of n.to_s.size in the first step? Let's run some benchmarks to find out.
def use_to_s(ndigits)
case ndigits
when 1
[1, 1]
else
a = 1
b = 2
idx = 3
loop do
break [idx, b] if b.to_s.length == ndigits
a, b = b, a + b
idx += 1
end
end
end
def use_digits(ndigits)
case ndigits
when 1
[1, 1]
else
a = 1
b = 2
idx = 3
loop do
break [idx, b] if b.digits.size == ndigits
a, b = b, a + b
idx += 1
end
end
end
require 'fruity'
def test(ndigits)
nfibs, last_fib = use_to_s(ndigits)
puts "\nndigits = #{ndigits}, nfibs=#{nfibs}, last_fib=#{last_fib}"
compare do
try_use_to_s { use_to_s(ndigits) }
try_use_digits { use_digits(ndigits) }
try_estimate { first_fibonacci_with_n_digits(ndigits) }
end
end
test 20
ndigits = 20, nfibs=93, last_fib=12200160415121876738
Running each test 128 times. Test will take about 1 second.
try_estimate is faster than try_use_to_s by 2x ± 0.1
try_use_to_s is faster than try_use_digits by 80.0% ± 10.0%
test 100
ndigits = 100, nfibs=476, last_fib=13447...37757 (90 digits omitted)
Running each test 16 times. Test will take about 4 seconds.
try_estimate is faster than try_use_to_s by 5x ± 0.1
try_use_to_s is faster than try_use_digits by 10x ± 1.0
test 500
ndigits = 500, nfibs=2390, last_fib=13519...63145 (490 digits omitted)
Running each test 2 times. Test will take about 27 seconds.
try_estimate is faster than try_use_to_s by 9x ± 0.1
try_use_to_s is faster than try_use_digits by 60x ± 1.0
test 1000
ndigits = 1000, nfibs=4782, last_fib=10700...27816 (990 digits omitted)
Running each test once. Test will take about 1 minute.
try_estimate is faster than try_use_to_s by 12x ± 10.0
try_use_to_s is faster than try_use_digits by 120x ± 100.0
There are two main take-aways from these results:
"try_estimate" is the fastest because it performs the first step relatively few times; and
the use of to_s is much faster than that of digits.
Further to the first of these observations note that the initial estimates of the index of the first FN having a given number of digits, compared to the actual index, are as follows:
for 20 digits: 96 est. vs 93 actual
for 100 digits: 479 est. vs 476 actual
for 500 digits: 2392 est. vs 2390 actual
for 1000 digits: 4785 est. vs 4782 actual
The deviation was at most 3, meaning numbers of digits had to be calculated for at most 3 FNs to obtain the desired result.
Explanation
The only explanation of the methods given in the section Code above is the derivation of the constant AVG_FNs_PER_DIGIT, which is used to calculate an estimate of the index of the first FN having the specified number of digits.
The derivation of this constant derives from the question and selected answer given here. (The Wiki for Fibonacci numbers provides a good overview of the mathematical properties of FNs.)
It is known that the first 7 FNs (including zero) have one digit; thereafter the FNs gain an additional digit every 4 or 5 FNs (i.e., sometimes 4, else 5). Therefore, as a very crude calculation, we see that to calculate the first FN with n digits, n >= 2, it will not be less than the 4*nth FN. For n = 1000, that would be 4,000. (In fact, the 4,782nd is the smallest to have 1,000 digits.) In other words, we don't need to calculate the number of digits in the first 4,000 FNs. We can improve on this estimate, however.
As n approaches infinity, the ratio of ranges 10**n...10**(n+1) (n-digit intervals) that contain 5 FNs to those that contain 4 FNs can be computed as follows.
LOG_10 = Math.log(10)
#=> 2.302585092994046
GR = (1 + Math.sqrt(5))/2
#=> 1.618033988749895
LOG_GR = Math.log(GR)
#=> 0.48121182505960347
RATIO_5to4 = (LOG_10 - 4*LOG_GR)/(5*LOG_GR - LOG_10)
#=> 3.6505564183095474
where GR is the Golden Ratio.
Over a large number of n-digit intervals let n4 be the number of those intervals containing 4 FNs and n5 be the number containing 5 FNs. The average number of FNs per interval is therefore (n4*4 + n5*5)/(n4 + n5). Since n5/n4 converges to RATIO_5to4, n5 approaches RATIO_5to4 * n4 in the limit (discarding roundoff error). If we substitute out n5, and let
b = 1/(1 + RATIO_5to4)
#=> 0.21502803321833364
we find the average number of FNs per n-digit interval converges to
avg = b * 4 + (1-b) *5
#=> 4.784971966781667
If fn is the first FN to have n decimal digits, the number of FNs in the sequence up to an including fn can therefore be approximated to be
n * avg
If, for example, the estimate of the index of the first FN to have 1000 decimal digits would be 1000 * 4.784971966781667).round #=> 4785.
In ruby, some large numbers are larger than infinity. Through binary search, I discovered:
(1.0/0) > 10**9942066.000000001 # => false
(1.0/0) > 10**9942066 # => true
RUBY_VERSION # => "2.3.0"
Why is this? What is special about 109942066? It doesn't seem to be an arbitrary number like 9999999, it is not close to any power of two (it's approximately equivelent to 233026828.36662442).
Why isn't ruby's infinity infinite? How is 109942066 involved?
I now realize, any number greater than 109942066 will overflow to infinity:
10**9942066.000000001 #=> Infinity
10**9942067 #=> Infinity
But that still leaves the question: Why 109942066?
TL;DR
I did the calculations done inside numeric.c's int_pow manually, checking where an integer overflow (and a propagation to Bignum's, including a call to rb_big_pow) occurs. Once the call to rb_big_pow happens there is a check whether the two intermediate values you've got in int_pow are too large or not, and the cutoff value seems to be just around 9942066 (if you're using a base of 10 for the power). Approximately this value is close to
BIGLEN_LIMIT / ceil(log2(base^n)) * n ==
32*1024*1024 / ceil(log2(10^16)) * 16 ==
32*1024*1024 / 54 * 16 ~=
9942054
where BIGLEN_LIMIT is an internal limit in ruby which is used as a constant to check if a power calculation would be too big or not, and is defined as 32*1024*1024. base is 10, and n is the largest power-of-2 exponent for the base that would still fit inside a Fixnum.
Unfortunately I can't find a better way than this approximation, due to the algorithm used to calculate powers of big numbers, but it might be good enough to use as an upper limit if your code needs to check validity before doing exponentiation on big numbers.
Original question:
The problem is not with 9942066, but that with one of your number being an integer, the other one being a float. So
(10**9942066).class # => Bignum
(10**9942066.00000001).class # => Float
The first one is representable by a specific number internally, which is smaller than Infinity. The second one, as it's still a float is not representable by an actual number, and is simply replaced by Infinity, which is of course not larger than Infinity.
Updated question:
You are right that there seem to be some difference around 9942066 (if you're using a 64-bit ruby under Linux, as the limits might be different under other systems). While ruby does use the GMP library to handle big numbers, it does some precheck before even going to GMP, as shown by the warnings you can receive. It will also do the exponentiation manually using GMP's mul commands, without calling GMP's pow functions.
Fortunately the warnings are easy to catch:
irb(main):010:0> (10**9942066).class
=> Bignum
irb(main):005:0> (10**9942067).class
(irb):5: warning: in a**b, b may be too big
=> Float
And then you can actually check where these warnings are emitted inside ruby's bignum.c library.
But first we need to get to the Bignum realm, as both of our numbers are simple Fixnums. The initial part of the calculation, and the "upgrade" from fixnum to bignum is done inside numeric.c. Ruby does quick exponentiation, and at every step it checks whether the result would still fit into a Fixnum (which is 2 bits less than the system bitsize: 62 bits on a 64 bit machine). If not, it will then convert the values to the Bignum realm, and continues the calculations there. We are interested at the point where this conversion happens, so let's try to figure out when it does in our 10^9942066 example (I'm using x,y,z variables as present inside the ruby's numeric.c code):
x = 10^1 z = 10^0 y = 9942066
x = 10^2 z = 10^0 y = 4971033
x = 10^2 z = 10^2 y = 4971032
x = 10^4 z = 10^2 y = 2485516
x = 10^8 z = 10^2 y = 1242758
x = 10^16 z = 10^2 y = 621379
x = 10^16 z = 10^18 y = 621378
x = OWFL
At this point x will overflow (10^32 > 2^62-1), so the process will continue on the Bignum realm by calculating x**y, which is (10^16)^621378 (which are actually still both Fixnums at this stage)
If you now go back to bignum.c and check how it determines if a number is too large or not, you can see that it will check the number of bits required to hold x, and multiply this number with y. If the result is larger than 32*1024*1024, it will then fail (emit a warning and does the calculations using basic floats).
(10^16) is 54 bits (ceil(log_2(10^16)) == 54), 54*621378 is 33554412. This is only slightly smaller than 33554432 (by 20), the limit after which ruby will not do Bignum exponentiation, but simply convert y to double, and hope for the best (which will obviously fail, and just return Infinity)
Now let's try to check this with 9942067:
x = 10^1 z = 10^0 y = 9942067
x = 10^1 z = 10^1 y = 9942066
x = 10^2 z = 10^1 y = 4971033
x = 10^2 z = 10^3 y = 4971032
x = 10^4 z = 10^3 y = 2485516
x = 10^8 z = 10^3 y = 1242758
x = 10^16 z = 10^3 y = 621379
x = 10^16 z = OWFL
Here, at the point z overflows (10^19 > 2^62-1), the calculation will continue on the Bignum realm, and will calculate x**y. Note that here it will calculate (10^16)^621379, and while (10^16) is still 54 bits, 54*621379 is 33554466, which is larger than 33554432 (by 34). As it's larger you'll get the warning, and ruby will only to calculations using double, hence the result is Infinity.
Note that these checks are only done if you are using the power function. That's why you can still do (10**9942066)*10, as similar checks are not present when doing plain multiplication, meaning you could implement your own quick exponentiation method in ruby, in which case it will still work with larger values, although you won't have this safety check anymore. See for example this quick implementation:
def unbounded_pow(x,n)
if n < 0
x = 1.0 / x
n = -n
end
return 1 if n == 0
y = 1
while n > 1
if n.even?
x = x*x
n = n/2
else
y = x*y
x = x*x
n = (n-1)/2
end
end
x*y
end
puts (10**9942066) == (unbounded_pow(10,9942066)) # => true
puts (10**9942067) == (unbounded_pow(10,9942067)) # => false
puts ((10**9942066)*10) == (unbounded_pow(10,9942067)) # => true
But how would I know the cutoff for a specific base?
My math is not exactly great, but I can tell a way to approximate where the cutoff value will be. If you check the above calls you can see the conversion between Fixnum and Bignum happens when the intermediate base reaches the limit of Fixnum. The intermediate base at this stage will always have an exponent which is a power of 2, so you just have to maximize this value. For example let's try to figure out the maximum cutoff value for 12.
First we have to check what is the highest base we can store in a Fixnum:
ceil(log2(12^1)) = 4
ceil(log2(12^2)) = 8
ceil(log2(12^4)) = 15
ceil(log2(12^8)) = 29
ceil(log2(12^16)) = 58
ceil(log2(12^32)) = 115
We can see 12^16 is the max we can store in 62 bits, or if we're using a 32 bit machine 12^8 will fit into 30 bits (ruby's Fixnums can store values up to two bits less than the machine size limit).
For 12^16 we can easily determine the cutoff value. It will be 32*1024*1024 / ceil(log2(12^16)), which is 33554432 / 58 ~= 578525. We can easily check this in ruby now:
irb(main):004:0> ((12**16)**578525).class
=> Bignum
irb(main):005:0> ((12**16)**578526).class
(irb):5: warning: in a**b, b may be too big
=> Float
Now we hate to go back to our original base of 12. There the cutoff will be around 578525*16 (16 being the exponent of the new base), which is 9256400. If you check in ruby, the values are actually quite close to this number:
irb(main):009:0> (12**9256401).class
=> Bignum
irb(main):010:0> (12**9256402).class
(irb):10: warning: in a**b, b may be too big
=> Float
Note that the problem is not with the number but with the operation, as told by the warning you get.
$ ruby -e 'puts (1.0/0) > 10**9942067'
-e:1: warning: in a**b, b may be too big
false
The problem is 10**9942067 breaks Ruby's power function. Instead of throwing an exception, which would be a better behavior, it erroneously results in infinity.
$ ruby -e 'puts 10**9942067'
-e:1: warning: in a**b, b may be too big
Infinity
The other answer explains why this happens near 10e9942067.
10**9942067 is not greater than infinity, it is erroneously resulting in infinity. This is a bad habit of a lot of math libraries that makes mathematicians claw their eyeballs out in frustration.
Infinity is not greater than infinity, they're equal, so your greater than check is false. You can see this by checking if they're equal.
$ ruby -e 'puts (1.0/0) == 10**9942067'
-e:1: warning: in a**b, b may be too big
true
Contrast this with specifying the number directly using scientific notation. Now Ruby doesn't have to do math on huge numbers, it just knows that any real number is less than infinity.
$ ruby -e 'puts (1.0/0) > 10e9942067'
false
Now you can put on as big an exponent as you like.
$ ruby -e 'puts (1.0/0) > 10e994206700000000000000000000000000000000'
false
I have a variable, between 0 and 1, which should dictate the likelyhood that a second variable, a random number between 0 and 1, is greater than 0.5. In other words, if I were to generate the second variable 1000 times, the average should be approximately equal to the first variable's value. How do I make this code?
Oh, and the second variable should always be capable of producing either 0 or 1 in any condition, just more or less likely depending on the value of the first variable. Here is a link to a graph which models approximately how I would like the program to behave. Each equation represents a separate value for the first variable.
You have a variable p and you are looking for a mapping function f(x) that maps random rolls between x in [0, 1] to the same interval [0, 1] such that the expected value, i.e. the average of all rolls, is p.
You have chosen the function prototype
f(x) = pow(x, c)
where c must be chosen appropriately. If x is uniformly distributed in [0, 1], the average value is:
int(f(x) dx, [0, 1]) == p
With the integral:
int(pow(x, c) dx) == pow(x, c + 1) / (c + 1) + K
one gets:
c = 1/p - 1
A different approach is to make p the median value of the distribution, such that half of the rolls fall below p, the other half above p. This yields a different distribution. (I am aware that you didn't ask for that.) Now, we have to satisfy the condition:
f(0.5) == pow(0.5, c) == p
which yields:
c = log(p) / log(0.5)
With the current function prototype, you cannot satisfy both requirements. Your function is also asymmetric (f(x, p) != f(1-x, 1-p)).
Python functions below:
def medianrand(p):
"""Random number between 0 and 1 whose median is p"""
c = math.log(p) / math.log(0.5)
return math.pow(random.random(), c)
def averagerand(p):
"""Random number between 0 and 1 whose expected value is p"""
c = 1/p - 1
return math.pow(random.random(), c)
You can do this by using a dummy. First set the first variable to a value between 0 and 1. Then create a random number in the dummy between 0 and 1. If this dummy is bigger than the first variable, you generate a random number between 0 and 0.5, and otherwise you generate a number between 0.5 and 1.
In pseudocode:
real a = 0.7
real total = 0.0
for i between 0 and 1000 begin
real dummy = rand(0,1)
real b
if dummy > a then
b = rand(0,0.5)
else
b = rand(0.5,1)
end if
total = total + b
end for
real avg = total / 1000
Please note that this algorithm will generate average values between 0.25 and 0.75. For a = 1 it will only generate random values between 0.5 and 1, which should average to 0.75. For a=0 it will generate only random numbers between 0 and 0.5, which should average to 0.25.
I've made a sort of pseudo-solution to this problem, which I think is acceptable.
Here is the algorithm I made;
a = 0.2 # variable one
b = 0 # variable two
b = random.random()
b = b^(1/(2^(4*a-1)))
It doesn't actually produce the average results that I wanted, but it's close enough for my purposes.
Edit: Here's a graph I made that consists of a large amount of datapoints I generated with a python script using this algorithm;
import random
mod = 6
div = 100
for z in xrange(div):
s = 0
for i in xrange (100000):
a = (z+1)/float(div) # variable one
b = random.random() # variable two
c = b**(1/(2**((mod*a*2)-mod)))
s += c
print str((z+1)/float(div)) + "\t" + str(round(s/100000.0, 3))
Each point in the table is the result of 100000 randomly generated points from the algorithm; their x positions being the a value given, and their y positions being their average. Ideally they would fit to a straight line of y = x, but as you can see they fit closer to an arctan equation. I'm trying to mess around with the algorithm so that the averages fit the line, but I haven't had much luck as of yet.
This seems horrible inefficient. Can someone give me a better Ruby way.
def round_value
x = (self.value*10).round/10.0 # rounds to two decimal places
r = x.modulo(x.floor) # finds remainder
f = x.floor
self.value = case
when r.between?(0, 0.25)
f
when r.between?(0.26, 0.75)
f+0.5
when r.between?(0.76, 0.99)
f+1.0
end
end
class Float
def round_point5
(self * 2).round / 2.0
end
end
A classic problem: this means you're doing integer rounding with a different radix. You can replace '2' with any other number.
Multiply the number by two.
round to whole number.
Divide by two.
(x * 2.0).round / 2.0
In a generalized form, you multiply by the number of notches you want per whole number (say round to .2 is five notches per whole value). Then round; then divide by the same value.
(x * notches).round / notches
You can accomplish this with a modulo operator too.
(x + (0.05 - (x % 0.05))).round(2)
If x = 1234.56, this will return 1234.6
I stumbled upon this answer because I am writing a Ruby-based calculator and it used Ruby's Money library to do all the financial calculations. Ruby Money objects do not have the same rounding functions that an Integer or Float does, but they can return the remainder (e.g. modulo, %).
Hence, using Ruby Money you can round a Money object to the nearest $25 with the following:
x + (Money.new(2500) - (x % Money.new(2500)))
Here, if x = $1234.45 (<#Money fractional:123445 currency:USD>), then it will return $1250.00 (#
NOTE: There's no need to round with Ruby Money objects since that library takes care of it for you!