The operators of the four arithmetic operations are common to most programming languages, +
-`` * ``/
, but the exponentiation operator varies depending on the programming language. In Ruby and Python, it is **
, but in BASIC it is ^
, and in the first place, there are many languages such as C and JavaScript before ES2016 that do not have a power operator.
reference:Exponentiation operator|Numeric type|Programming language comparison|hydrocul notes
By the way, in some areas, exponentiation is represented by "↑". That is $ 2 \ uparrow 4 = 2 ^ 4 = 16 $. So let's make it possible to calculate exponentiation with "↑" even in Ruby.
Unfortunately, no matter how flexible Ruby is as a programming language, you can't create new operators yourself. [^ 1] So, please forgive the point where you want to write "2 ↑ 4" with a dot like 2. ↑ 4
.
[^ 1]: I can't ... right? Please comment if there is a way.
class Integer
def ↑(n)
self ** n
end
end
puts 2.↑ 4 # => 16 (2 to the 4th power)
You can do this because Ruby can also use non-ASCII characters in method names. [^ 2]
[^ 2]: It is good manners to use Refinement when extending a built-in class like this, but since the method_missing
that will be used later cannot be used in Refinement, it directly extends Integer.
In Ruby 3.0, you can also use the endless method definition to write:
def ↑(n) = self ** n
By the way, exponentiation was a repetition of multiplication.
2 \uparrow 4 = 2^4 = \underbrace{2 \times 2 \times 2 \times 2}_{4 2}
Then, you will want to think about the operation of "repetition of exponentiation". In some neighborhoods, it is called "tetration" and is represented by "↑↑".
2 \uparrow\uparrow 4 = \underbrace{2 \uparrow 2 \uparrow 2 \uparrow 2}_{4 2} = 2^{2^{2^2}} = 2^{2^4} = 2^{16} = 65536
So I would like to be able to calculate the titration with 2. ↑↑ 4
. If that's the only purpose, you can write it like this.
def ↑↑(n)
Array.new(n, self).inject{ |result, item| item.↑ result }
end
I'd like to write Array.new (n, self) .inject (: ↑)
, but this doesn't work. Since power is right-associative, it should be $ 3 \ uparrow 3 \ uparrow 3 = 3 \ uparrow (3 \ uparrow 3) = 3 \ uparrow 27 = 7625597484987 $, but Array.new (n, self) .inject ( : ↑)
If it is, it means $ (3 \ uparrow 3) \ uparrow 3 = 27 \ uparrow 3 = 19683 $ in the calculation order.
Now, we have defined an operation called titration that repeats exponentiation. Then you want to define the operation of repeating tetration. It is called "pentation" and is represented by "↑↑↑". In other words
2 \uparrow\uparrow\uparrow 4 = \underbrace{2 \uparrow\uparrow 2 \uparrow\uparrow 2 \uparrow\uparrow 2}_{4 2}
It is that.
If you're writing a program that just does pentation, copy and paste the code above and tweak it a bit
def ↑↑↑(n)
Array.new(n, self).inject{ |result, item| item.↑↑ result }
end
It would be good.
However, as you can imagine, there is a "↑↑↑↑" (hexation) beyond the pentation. Furthermore, you can extend the arrow infinitely with "↑↑↑↑↑", "↑↑↑↑↑↑" ...
It is possible to define the operation one level higher by copying and pasting with the above method, but it is really troublesome to define up to "↑↑… (100 ↑)… ↑↑". A possible method in such cases is dynamic method definition using define_method
.
class Integer
2.upto(100) do |i|
define_method("↑" * i) do |n|
operator = "↑" * (i - 1)
Array.new(n, self).inject{ |result, item| item.send(operator, result) }
end
end
end
This way, you can easily define from "↑↑" to "↑↑ ... (100 ↑) ... ↑↑".
However, after defining the methods up to "↑↑… (100 ↑)… ↑↑" in this way, there may be an opportunity to need “↑↑… (101 ↑)… ↑↑”. [^ 3] No matter how many "↑↑… ↑↑" are needed, I want to be able to handle them without rewriting the program.
[^ 3]: Probably not.
That's where the Ruby black art royal road method_missing
comes into play. [^ 4]
[^ 4]: As you can see in the Official Documentation, when defining method_missing
, also definerespond_to_missing?
.
class Integer
def ↑(n)
self ** n
end
def method_missing(name, *args)
return super if name.match?(/[^↑]/)
unless args.size == 1
raise ArgumentError,
"wrong number of arguments (given #{args.size}, expected 1)"
end
operator = name[0..-2]
n = args[0]
Array.new(n, self).inject do |result, item|
item.send(operator, result)
end
end
def respond_to_missing?(symbol, include_private)
symbol.match?(/[^↑]/) ? super : true
end
end
With this, we can handle whether it is titration, pentation, hexation, or "↑↑ ... (1000 ↑) ... ↑↑"!
Then, let's start by calculating lightly with 3 ↑↑↑ 3!
> puts 3.↑↑↑ 3
bignum too big to convert into `long' (RangeError)
Oh...[^5]
[^ 5]: This number is called Toritori, and even if the particles of the entire universe are used as memory, it is a number that can never be expressed. Reference: Kamen Rider Build Formula Episode 16 Tetration
Now that we have defined the arrow notation, let's write a program to find the Graham's number, which is famous as a huge number in Guinness. Of course, it is a larger number than 3 ↑↑↑ 3, so it cannot be actually calculated, but it is possible if the calculation procedure is simply expressed as a program.
First, define a function $ g (n) = 3 \ underbrace {\ uparrow \ uparrow \ cdots \ uparrow \ uparrow} _ {n \ uparrow} 3 $, commonly called the Graham function.
def g(n)
3.send("↑" * n, 3)
end
By defining the arrow notation using method_missing, the Graham function can be easily expressed in this way.
Graham's number is expressed using the Graham function as $ G = \ underbrace {g (g (\ cdots g (g (4)) \ cdots))} _ {64 g ()} $. The composition of the same function may be written as $ g ^ {64} (4) $ as a power.
If so, shouldn't we be able to write the composition of the same function in Ruby using the exponentiation operator **
as in method ** 64
?
class Method
def **(times)
Array.new(times, self).inject(:>>)
end
end
Now the program for Graham's number is ready.
g64 = method(:g) ** 64
puts g64.call(4)
I was able to write a program to find Graham's number very simply!
I think the flexibility of Ruby goes well with mathematics, so if you are a mathematician, why not try using Ruby?
Recommended Posts