[PYTHON] Musik, die von rekursiven Funktionen gespielt wird

Haben Sie jemals ** "rekursive Funktionen" "gehört"? ** ** **

Ich denke, es ist selten, dass ich es gehört habe, also genieße bitte die Musik ein wenig.

Musik gemacht

Klicken Sie auf das ** Bild unten, um YouTube ** zu öffnen. Die Miniaturansichten sind gleich, aber ein anderer Link wird ordnungsgemäß geöffnet.

Taraimawashi-Funktion

tarai

Tak-Funktion

tak

3-variable Ackermann-Funktion

tak

Der Anfang der Geschichte

Im Jahr 2011 wurde ein Artikel "Musikgenerierung mit Takeuchi-Funktion" veröffentlicht, um Musik mit Takeuchi-Funktion (Taramawashi-Funktion) zu machen. Das Folgende ist das Video, und Sie können sehen, dass die rekursive Funktion gute Musik macht.

tak ↑ Originalvideo

Takeuchi-Funktion (Taramawashi-Funktion)

Es ist eine rekursive Funktion wie die folgende.

\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}

Wenn Sie zunächst fragen, was eine rekursive Funktion ist, lesen Sie zunächst Kenchos Artikel "Welche Art von Welt wird sich erweitern, wenn Sie rekursive Funktionen lernen". Ich denke, es ist perfekt durch.

Wie Sie der Definition entnehmen können, ruft die Takeuchi-Funktion den Prozess rekursiv auf, aber das Argument $ z $ wird in der if-Anweisung nicht verwendet, sodass es sehr verschwenderisch ist. Wenn Sie beispielsweise $ Tarai (10, 5, 0) $ aufrufen, erhalten Sie 343.073 rekursive Aufrufe. Der Grund, warum dies mit Musik zusammenhängt, ist unten angegeben.

Ich habe ein Programm geschrieben, um herauszufinden, wie sich die Argumente dieses Funktionsaufrufs ändern, und im Fall von Tarai (10,5,0) ist jedes der drei Argumente 0 bis 10 (x ist -1 bis ~). Während sich zwischen 10) nach und nach etwas ändert, gibt es ein Verhalten, bei dem zwei Werte festgelegt sind und ein Wert sinkt, was etwas an die Akkordfolge der drei Musikakkorde erinnert. Es ist eins.   In diesem Fall habe ich es tatsächlich als Ton gehört. Jedes Mal, wenn die Tarai-Funktion aufgerufen wird, werden die Argumente x, y, z Klängen wie 0 = mi, 1 = fa, 2 = so, ... zugewiesen und klingen als 3 Akkorde. Da es eine große Sache ist, lässt die automatische Tonquelle Arpeggio es minimal aussehen. Ich bin. Ich denke, es ist ein wenig interessant für diese Art von automatisch generierter Musik, weil sich die Spannung ändert.

Einfallsreichtum, um Musik zu machen

** Es scheint, dass ich Musik gemacht habe, indem ich die Nummer des Arguments der rekursiven Funktion Doremi entsprechen ließ **. Siehe die folgende Tabelle.

Zahlen -1 0 1 2 3 4 5 6 7 8 9 10
Klang Les Mi. Fa Damit La Shi Machen Les Mi. Fa Damit La

Nach Angaben des Antragstellers

Also habe ich beschlossen, dies nachzuahmen. Bis zu diesem Punkt die vorhandenen Inhalte.

Versuchen Sie zu implementieren

Deshalb habe ich es tatsächlich versucht.

Ich kenne MIDI nicht, deshalb habe ich die Python-Retro-Game-Engine Pyxel verwendet. Der Code unten (etwas lang). Außerdem verwendet diese Implementierung die † böse globale Variable †. Überprüfen Sie daher [Referenz: Implementierung mit Abschluss](#Referenz: Implementierung mit Abschluss), um ein besseres Implementierungsbeispiel zu erhalten.

import pyxel
import sys
sys.setrecursionlimit(10_000_000) #Erhöhen Sie die obere Wiederholungsgrenze

#† Böse globale Variablen †
cnt = 0  #Anzahl rekursiver Anrufe

# -1:Les, 0:Mi., ....Entsprechen
note = ["C3", "D3", "E3", "F3", "G3",
        "A4", "B4", "C4", "D4", "E4", "F4", "G4",
        "B3"]  # -Ton entsprechend 1

#Takeuchi-Funktionsargument als Notiz
melody_x = ""
melody_y = ""
melody_z = ""

#Behalten Sie die Argumente für das Zeichnen bei
xs = []
ys = []
zs = []


def tarai(x, y, z):
    """tarai(10, 5, 0)Ist-Wechseln Sie von 1 zu 10
    """
    global cnt
    global melody_x, melody_y, melody_z
    global xs, ys, zs
    
    cnt += 1
    if cnt >= 500:
        raise StopIteration("Ende des rekursiven Aufrufs -")

    #Argumente halten
    melody_x += note[x]
    melody_y += note[y]
    melody_z += note[z]
    xs.append(x)
    ys.append(y)
    zs.append(z)

    #Rekursive Verarbeitung
    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):
    """Definieren Sie Musik entlang einer Funktion
    """
    global melody_x, melody_y, melody_z
    SPEED = 20  #120 für eine Note pro Sekunde,120 pro Sekunde/GESCHWINDIGKEIT mal

    #Nehmen Sie bis zu 500 Wiederholungen auf
    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_Unzulässiger Name: Tarai, tak,Bitte geben Sie eine Bestätigung an")
    except ValueError as e:
        print(e)
    except StopIteration as e:
        print(e)

    #Definieren Sie Musik nach jedem 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():
    """Zeichnen Sie die Tastatur
    """
    _width = pyxel.width//14  #Weil es 14 Schlüssel gibt
    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-Funktionsargumente+2)Entspricht der Position der Tastatur
    #Färben Sie die Tasten, die Sie spielen
    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)

    #Zeichnen Sie die Tastatur
    for i in range(14):
        #Weißer Schlüssel
        pyxel.rectb(x=2+_width*i, y=20, w=_width,
                    h=60, col=pyxel.COLOR_BLACK)
        #Schwarzer Schlüssel
        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) #spiel Musik
    pyxel.run(update, draw)

Ich hörte so ein Geräusch. (Veröffentlichen Sie den ersten Link im Artikel erneut. Klicken Sie zum Spielen).

tarai

** Ich konnte es nicht so schön einstellen wie die Musik des Antragstellers, aber es sieht so aus! Beeindruckt! ** ** **

Aber hier zu enden ist nichts Neues. Ich bin krank.

Ich möchte auch andere rekursive Funktionen hören! !!

** Ich dachte: "Wenn die Takeuchi-Funktion verwendet werden kann, können andere Funktionen verwendet werden?" **.

Was kam mir in den Sinn, als ich hörte, dass es rekursiv war

--Ackerman-Funktion --Fibonatch-Nummernfolge

Eine solche. Die folgenden Bedingungen wurden gegeben, um Musik zu machen.

  1. ** Muss 3 Argumente haben **
  2. ** Der Bereich möglicher Werte für das Argument beträgt 88 oder weniger **

Bedingung 1 ist, weil wir 3 Akkorde wie die Taramawashi-Funktion früher machen wollen, und Bedingung 2 ist, weil es schwierig ist, mehr als die Anzahl der Klaviertasten zuzuordnen. (Nein, Sie können den Überschuss so oft verwenden, wie Sie möchten, aber er ist süß.)

Deshalb habe ich einen Typen gemacht, der gut aussieht.

Tak-Funktion

(Plötzlich eine Nische ...)

Laut Wikipedia scheint es sich um eine Funktion zu handeln, die durch ein Missverständnis der Takeuchi-Funktion entstanden ist.

[John McCarthy](https://ja.wikipedia.org/wiki/John McCarthy) hat die Takeuchi-Funktion geändert, um aufgrund von Missverständnissen, die als ** Tak-Funktion ** verbreitet wurden, $ z $ zurückzugeben. .. Das Folgende ist die 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}}}

>
 > Viel weniger Berechnung (z. B. Tarai (12, 6, 0) ruft Tarai 12.604.860 Mal auf, während Tak (12, 6, 0) Tak nur 63.608 Mal aufruft).

 Das Implementierungsbeispiel lautet wie folgt.

```python
def tak(x, y, z):
    """tak ist tarais Rückkehr y als z
    """
    if x <= y:
        return z  #Dies unterscheidet sich von Tarai
    else:
        return tak(tak(x - 1, y, z),
                   tak(y - 1, z, x),
                   tak(z - 1, x, y))

Musik ist unten (zum Abspielen klicken)

tarai Irgendwie fühle ich mich ** anders schmecken **.

3-variable Ackermann-Funktion

Gewöhnliche Ackerman-Funktion

Ist die Ackermann-Funktion dafür bekannt, lächerlich große Zahlen (große Zahlen) erzeugen zu können? **. Die Definition ist wie folgt.

{\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}}}

Lassen Sie uns ein wenig abweichen, aber schauen wir uns auch die Wertetabelle an.

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 2^{65536} − 3 {\displaystyle {2^{2^{65536}}}-3} A(3, A(4, 3)) = 2^{2^{2^{65536}}} − 3
5 65533 {\displaystyle {\begin{matrix}\underbrace {{2^{2}}^{{\cdot }^{{\cdot }^{{\cdot }^{2}}}}} -3 \\\ 65536{\text{ twos}}\end{matrix}}} A(4, A(5, 1)) A(4, A(5, 2)) A(4, A(5, 3))

Zum Zeitpunkt von $ Ack (4, 2) $ gibt es 19.729 Ziffern, und von der Mitte aus ist es nicht möglich, mathematische Formeln in allgemeiner Notation auszudrücken.

Ruhige Gesprächspause. Dies ist eine interessante Funktion, hat aber nur zwei Argumente **…! Aber ich möchte es benutzen!

Multivariabel

Als ich hartnäckig nachforschte, wurde auch eine multivariable Version vorgeschlagen. ([Multivariate Ackerman-Funktion]([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 / multivariate Ackermann-Funktion))))

** Multivariate Ackermann-Funktion ** A (x1, x2,…, xn) A (x1, x2,…, xn) wurde 2007 von Taro entwickelt und in Fishshu (2013) eingeführt. Es war. Wenn zwei Variablen vorhanden sind, entspricht dies der Ackerman-Funktion, und wenn drei oder mehr Variablen vorhanden sind, handelt es sich um die Array-Notation. Es handelt sich um eine [multiple Regressionsfunktion](https://googology.wikia.org/ja/wiki/multiple Regressionsfunktion), die dieselbe Steigerungsrate aufweist wie die Notation ja / wiki / array).

Wenn Sie den Fall von 3 Variablen aufschreiben, wird dieser wie folgt multipliziert.

{\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}}}

Es ist zu schnell, als dass die Zahlen zu groß werden könnten, also habe ich $ Ack (1,1,1) $ gespielt. Der Maximalwert beträgt jedoch 61 und die Anzahl der Wiederholungen 2440, was eine lächerliche Funktion ist.

Implementierungsbeispiel

def ack(l, m, n):
    """3-variable Version der Ackerman-Funktion
    """
    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 (zum Abspielen klicken)

tarai

Wie Sie beim Hören sehen können, ändern sich x und y kaum. Da x und y nur 0 oder 1 sind, ist es eine eintönige Musik, aber es macht Spaß, weil sich der Rhythmus plötzlich in der Mitte ändert. ** ** **

Zusammenfassung

** Ich habe Musik mit einer rekursiven Funktion gespielt. ** Musik ist ein Amateur, aber es macht Spaß, den Ton zu hören! In diesem Artikel habe ich nur drei Funktionen ausprobiert, die ich mir ausgedacht habe: "Was passiert, wenn Sie mit dem Suchalgorithmus ein Geräusch machen?" "Klingt es nicht besser so?" Usw. ** Ich möchte, dass Sie es selbst tun und es melden! Ich habe es auch getan **

Wenn Sie Fragen haben, lassen Sie es uns bitte in den Kommentaren wissen.

Referenz: Implementierung mit Closure

Es gibt eine Methode, die den Abschluss als Methode zum Zählen der Anzahl rekursiver Aufrufe ohne Verwendung der globalen Variablen verwendet. Durch Definieren einer rekursiven Funktion innerhalb einer Funktion kann die Anzahl der Aufrufe auf "nicht lokal" anstatt auf "global" gesetzt werden. Die folgende Funktion zählt als Beispiel die Anzahl der Anrufe.

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

Für weitere Informationen googeln Sie bitte mit Closure oder "nonlocal".

Ich hatte nicht vor, eine Beschreibung von Verschlüssen oder "nichtlokalen" in den Artikel aufzunehmen, deshalb habe ich eine grobe, aber leicht verständliche Implementierung vorgenommen. Wenn das Implementierungsbeispiel der globalen Variablen unbeliebt ist, werde ich es ersetzen, daher würde ich es begrüßen, wenn Sie einen Kommentar abgeben könnten.

Recommended Posts

Musik, die von rekursiven Funktionen gespielt wird
Aktivieren Sie Aktivierungsfunktionen nebeneinander
Memorandum über das Auswendiglernen rekursiver Funktionen