Have you ever "listened" to ** "recursive functions"? ** **
I think it's rare to have heard it, so please enjoy the music a little.
Click the ** image below to open youtube **. The thumbnails are the same, but a different link opens up properly.
In 2011, an article "Music generation with Takuchi function" was published to make music with Takuchi function. Below is the video, where you can see that the recursive function makes good music.
The recursive function is as follows.
\begin{eqnarray}
\text{Tarai}(x,y,z)=\left\{ \begin{array}{ll}
y & (x \leq y) \\
\text{Tarai}(\text{Tarai}(x-1,y,z),\text{Tarai}(y-1,z,x),\text{Tarai}(z-1,x,y)) & (otherwise) \\
\end{array} \right.
\end{eqnarray}
In the first place, if you are asking "what is a recursive function?", See Kencho's article "What kind of world will expand when you learn recursive functions". I think it's perfect if you let it through.
As you can see from the definition, the Takeuchi function calls the process recursively, but the argument $ z $ is not used in the if statement, so it is very wasteful. For example, if you call $ Tarai (10, 5, 0) $, the recursive call will be 343,073 times. The reason why this is related to music is quoted below.
I wrote a program to find out how the arguments of this function call change, and in the case of Tarai (10,5,0), each of the three arguments is 0 to 10 (x is -1 to ~). While changing little by little between 10), there is a behavior that two values are fixed and one value goes down, which is somewhat reminiscent of the chord progression of the three chords of music. It is one. In that case, I actually listened to it as a sound. Every time the Tarai function is called, the arguments x, y, and z are assigned to sounds like 0 = mi, 1 = fa, 2 = so, ..., and sounded as 3 chords. Since it's a big deal, the automatic sound source Arpeggio makes it look minimal. I am. I think it's a little interesting for this kind of automatically generated music because there is a change in tension.
** It seems that I made music by making the number of the argument of the recursive function correspond to Doremi **. See the table below.
Numbers | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
sound | Re | Mi | Fa | So | La | Shi | Do | Re | Mi | Fa | So | La |
According to the proposer
--Use only white keys (do not use #) --The minimum value (-1) was set to the durian scale. --The tempo and tone have been devised
So I decided to imitate this. Up to this point, the existing contents.
That's why ** I actually tried it **.
I don't know MIDI, so I used the Python retro game engine Pyxel. The code below (slightly long). Also, this implementation uses the † evil Global variable †, so check [Reference: Implementation using closures](#Reference: Implementation using closures) for a better implementation example.
import pyxel
import sys
sys.setrecursionlimit(10_000_000) #Raise the upper limit of recursion
#† Evil global variables †
cnt = 0 #Recursive call count
# -1:Re, 0:Mi, ....Correspond to
note = ["C3", "D3", "E3", "F3", "G3",
"A4", "B4", "C4", "D4", "E4", "F4", "G4",
"B3"] # -Sound corresponding to 1
#Takeuchi function argument as a musical note
melody_x = ""
melody_y = ""
melody_z = ""
#Keep arguments for drawing
xs = []
ys = []
zs = []
def tarai(x, y, z):
"""tarai(10, 5, 0)Is-Change from 1 to 10
"""
global cnt
global melody_x, melody_y, melody_z
global xs, ys, zs
cnt += 1
if cnt >= 500:
raise StopIteration("End of recursive call ―")
#Hold arguments
melody_x += note[x]
melody_y += note[y]
melody_z += note[z]
xs.append(x)
ys.append(y)
zs.append(z)
#Recursive processing
if x <= y:
return y
else:
return tarai(tarai(x - 1, y, z),
tarai(y - 1, z, x),
tarai(z - 1, x, y))
def define_music(func_name: str):
"""Define music along a function
"""
global melody_x, melody_y, melody_z
SPEED = 20 #120 for one note per second,120 per second/SPEED times
#Record up to 500 recursion
try:
if func_name == "tarai":
tarai(10, 5, 0)
elif func_name == "tak":
tak(10, 5, 0)
elif func_name == "ack":
ack(1, 1, 1)
else:
raise ValueError("func_Illegal name: tarai, tak,Please specify one of ack")
except ValueError as e:
print(e)
except StopIteration as e:
print(e)
#Define music according to each argument
pyxel.sound(2).set(
note=melody_x,
tone="s",
volume="5",
effect="n",
speed=SPEED,
)
pyxel.sound(3).set(
note=melody_y,
tone="t",
volume="5",
effect="f",
speed=SPEED,
)
pyxel.sound(4).set(
note=melody_z,
tone="n",
volume="5",
effect="f",
speed=SPEED,
)
pyxel.music(0).set([], [2], [3], [4])
def update():
pass
def draw():
"""Draw the keyboard
"""
_width = pyxel.width//14 #Because there are 14 keys
pyxel.cls(pyxel.COLOR_LIGHTGRAY)
pyxel.rect(x=2, y=20, w=_width*14, h=60, col=pyxel.COLOR_WHITE)
f = (pyxel.frame_count//5) % len(xs)
# (Takeuchi function arguments+2)Corresponds to the location of the keyboard
#Color the keyboard you play
pyxel.rect(x=2+_width*(xs[f]+2), y=20, w=_width,
h=60, col=pyxel.COLOR_CYAN)
pyxel.rect(x=2+_width*(ys[f]+2), y=20, w=_width,
h=60, col=pyxel.COLOR_LIME)
pyxel.rect(x=2+_width*(zs[f]+2), y=20, w=_width,
h=60, col=pyxel.COLOR_YELLOW)
#Draw the keyboard
for i in range(14):
#White key
pyxel.rectb(x=2+_width*i, y=20, w=_width,
h=60, col=pyxel.COLOR_BLACK)
#Black key
if i % 7 in [0, 1, 3, 4, 5]:
pyxel.rect(x=2+_width*(i+2/3), y=20, w=(_width*2/3),
h=30, col=pyxel.COLOR_BLACK)
if __name__ == "__main__":
pyxel.init(width=200, height=100, fps=30)
define_music(func_name="tarai")
pyxel.playm(0, loop=True) #play music
pyxel.run(update, draw)
I heard a sound like this. (Repost the first link in the article. Click to play).
** I couldn't adjust it as cleanly as the proposer's music, but it looks like it! Impressed! ** **
But ending here is nothing new. I'm sick.
** I thought, "If the Takeuchi function can be used, other functions can be used?" **.
What came to my mind when I heard that it was recursion
--Ackermann function --Fibonacci sequence --Repeated self-multiplication method --Depth-first search --Recurrence formula
Such. The following conditions were given in order to make it music.
Condition 1 is because we want to make triads like the previous Taramawashi function, and condition 2 is because it is troublesome to associate more than the number of piano keys. (No, you can use the surplus as much as you want, but it's cute)
That's why I made a guy that looks good.
(Suddenly a niche guy ...)
According to wikipedia, it seems that ** a function was created by misunderstanding the Takeuchi function **.
[John McCarthy](https://ja.wikipedia.org/wiki/John McCarthy) changed the Takeuchi function to return $ z $ due to misremembering, which became popular as the ** Tak function **. .. The following is the definition.
{\displaystyle {\rm {Tak}}(x,y,z)={\begin{cases}z&{\mbox{ if }}x\leq y\{\rm {Tak}}({\rm {Tak}}(x-1,y,z),{\rm {Tak}}(y-1,z,x),{\rm {Tak}}(z-1,x,y))&{\mbox{ otherwise. }}\\end{cases}}}
>
> The complexity is much smaller (for example, tarai (12, 6, 0) calls tarai 12,604,860 times, while tak (12, 6, 0) calls tak only 63,608 times).
The implementation example is as follows.
```python
def tak(x, y, z):
"""tak is tarai's return y as z
"""
if x <= y:
return z #This is different from tarai
else:
return tak(tak(x - 1, y, z),
tak(y - 1, z, x),
tak(z - 1, x, y))
Music is below (click to play)
Somehow I feel ** different taste **.
Is the Ackermann function famous for creating ridiculously large numbers (large numbers)? **. The definition is as follows.
{\displaystyle \operatorname {Ack} (m,n)={\begin{cases}n+1,&{\text{ if }}m=0\\\operatorname {Ack} (m-1,1),&{\text{ if }}n=0\\\operatorname {Ack} (m-1,\operatorname {Ack} (m,n-1)),&{\text{ otherwise}}\\\end{cases}}}
Let's derail a little, but let's also look at the table of values.
m\n | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 |
1 | 2 | 3 | 4 | 5 | 6 |
2 | 3 | 5 | 7 | 9 | 11 |
3 | 5 | 13 | 29 | 61 | 125 |
4 | 13 | 65533 | A(3, A(4, 3)) = |
||
5 | 65533 | A(4, A(5, 1)) | A(4, A(5, 2)) | A(4, A(5, 3)) |
At the time of $ Ack (4, 2) $, there are 19,729 digits, and from the middle, it is not possible to express mathematical formulas with general notation.
Quiet talk break. This is an interesting function, but it has only two arguments **…! But I want to use it!
When I stubbornly investigated, ** a multivariable version was also proposed **. ([Multivariable Ackermann Function]([https://googology.wikia.org/ja/wiki/%E5%A4%9A%E5%A4%89%E6%95%B0%E3%82%A2%E3%] 83% 83% E3% 82% AB% E3% 83% BC% E3% 83% 9E% E3% 83% B3% E9% 96% A2% E6% 95% B0](https://googology.wikia.org) / ja / wiki / multivariable Ackermann function))))
** Multivariable Ackermann function ** A (x1, x2,…, xn) A (x1, x2,…, xn) was devised by Taro in 2007 and introduced in Fishshu (2013). It was. It is the same as [Ackermann function](https://googology.wikia.org/ja/wiki/Ackermann function) when it is 2 variables, and Array notation when it is 3 variables or more. It is a [multiple regression function](https://googology.wikia.org/ja/wiki/multiple regression function) that has the same rate of increase as ja / wiki / array notation).
If you write down the case of 3 variables, it will be multiplied as follows.
{\displaystyle \operatorname {Ack} (l, m,n)=
{\begin{cases}
n+1,&{\text{ if }}m=n=0\\
\operatorname {Ack} (l, m-1,1),&{\text{ if }}n=0\\
\operatorname {Ack} (l-1, n, n),&{\text{ if }}m=0\\
\operatorname {Ack} (l, m-1, \operatorname {Ack} (l, m,n-1)),&{\text{otherwise}}\\
\end{cases}}}
It's too fast for the numbers to get too big, so I played $ Ack (1,1,1) $. However, the maximum value is 61 and the number of recursion is 2440, so it is a ridiculous function.
Implementation example
def ack(l, m, n):
"""3-variable version of the Ackermann function
"""
if l == 0 and m == 0:
return n+1
elif n == 0:
return ack(l, m-1, 1)
elif m == 0:
return ack(l-1, n, n)
else:
return ack(l, m-1, ack(l, m, n-1))
Video (click to play)
As you can see by listening, x and y hardly change. Since x and y are only 0 or 1, it's a little monotonous music, but it's fun because the rhythm suddenly changes in the middle. ** **
** I played music with a recursive function. ** Music is an amateur, but it's fun to hear the sound! In this article I've tried only three functions I came up with, "What happens if you make a sound with a search algorithm?" "Doesn't it sound better this way?" Etc. ** I want you to do it yourself and report it! I did it too **
If you have any questions, please let us know in the comments.
There is a method that uses closures as a method of counting the number of recursive calls without using Global variables. By defining a recursive function inside a function, the number of calls can be set to nonlocal
instead of global
. The following is a function that counts the number of calls as an example.
def count_tarai(x, y, z) -> int:
count = 0
def tarai(x, y, z):
nonlocal count
count += 1
if x <= y:
return y
else:
return tarai(tarai(x-1, y, z),
tarai(y-1, z, x),
tarai(z-1, x, y))
tarai(x, y, z)
return count
For more information, please google for closures or nonlocal
.
I didn't intend to include closures or nonlocal
descriptions in the article, so I've included a crude but easy-to-understand implementation. If the implementation example of global variables is unpopular, I will replace it, so I would appreciate it if you could comment.