Pchełki VBA: rysujemy spiralę Ulama w Excelu

Excel to wspaniałe narzędzie. Możemy w nim robić całkiem zaawansowane rzeczy: modele statystyczne, finansowe, symulacje, nawet średnią z kilku liczb jak się dobrze postaramy. No wypas.

Do rysowania piksel po pikselu Excel nadaje się jak pantograf do wyrabiania masła. Da się, ale co się człowiek przy tym namęczy to jego.

Niemniej jednak na niedawną prośbę jednego ze stałych czytelników blogu oto prezentuję WSWAJDMDRSUWE (Wolny, Słabo Wyglądający, Ale Jednak Działający Moduł Do Rysowania Spirali Ulama W Excelu):

   
Option Explicit

Public Sub Ulam1(ByVal r As String, ByVal maxn As Long)
' r: adres srodka spirali
' maxn: do ilu liczymy
    Dim n As Long ' aktualna liczba (1..maxn)
    Dim k As Integer ' dlugosc aktualnej krawedzi
    Dim dx As Integer, dy As Integer  ' kierunek kolejnego kroku; dx w poziomie, dy w pionie (dodatnie: prawo-gora, ujemne: lewo-dol)
    Dim i As Long ' skrypt bez i jest jak ryba bez roweru
    
    Dim sh As Excel.Range ' sh od "snake head" czyli robocza komorka w ktorej aktualnie jestesmy
    
    Set sh = Range(r) ' zaczynamy od srodka
    dx = 0 'zaczynamy od ruchu w gore
    dy = 1
    k = 1  'o jeden krok
    
    ThisWorkbook.ActiveSheet.Cells.Clear
    ActiveWindow.Zoom = 10
    ThisWorkbook.ActiveSheet.Cells.ColumnWidth = 5
    ThisWorkbook.ActiveSheet.Cells.RowHeight = 24
    
    Do While n < = maxn
        For i = 1 To k
            sh.Value = n
            If CzyPierwsza(n) Then
                sh.Interior.Color = RGB(0, 0, 0)
                sh.Font.Color = RGB(255, 255, 255)
            Else
                sh.Interior.Color = RGB(255, 255, 255)
                sh.Font.Color = RGB(0, 0, 0)
            End If
            n = n + 1
            If n Mod 100 = 0 Then DoEvents
            Set sh = sh.Offset(-dy, dx)
        Next i
        ' koniec krawedzi - zakrecamy
        If dx = 0 And dy = 1 Then 'gora=>prawo
            dx = 1
            dy = 0
        ElseIf dx = 0 And dy = -1 Then ' dol=>lewo
            dx = -1
            dy = 0
        ElseIf dx = 1 And dy = 0 Then ' prawo=>dol, krawedz + 1
            dx = 0
            dy = -1
            k = k + 1
        ElseIf dx = -1 And dy = 0 Then ' lewo=>gora, krwedz + 1
            dx = 0
            dy = 1
            k = k + 1
        Else ' cos sie spiertenteges
            MsgBox "!", vbOKOnly + vbExclamation
            End
        End If
    Loop
End Sub

Public Function CzyPierwsza(ByVal n As Long) As Boolean
    If n < 2 Then
        CzyPierwsza = False
    ElseIf n = 2 Or n = 3 Then CzyPierwsza = True
    ElseIf n Mod 2 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 3 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 5 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 7 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 11 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 13 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 17 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 19 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 23 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 29 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 31 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 37 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 41 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 43 = 0 Then
        CzyPierwsza = False
    ElseIf n Mod 47 = 0 Then
        CzyPierwsza = False
    Else
        Dim p As Integer ' pierwiastek
        p = Sqr(n) + 1
        Dim i As Integer
        For i = 53 To p Step 2
            If n Mod i = 0 Then
                CzyPierwsza = False
                Exit Function
            End If
        Next i
        CzyPierwsza = True
    End If
End Function
     

Ponieważ jestem leniwy, nie będę tu omawiał całego skryptu. Dodam tylko, że nieelegancka jest nie tylko pętla główna, ale również metoda sprawdzania pierwszości liczb (od 2 do 2207 mamy zakodowane na stałe sito Eratostenesa, powyżej tego po prostu sprawdzamy podzielność przez kolejne nieparzyste od 53 w górę aż do pierwiastka ze sprawdzanej liczby). Jednak skrypt jakimś cudem działa, więc chociaż trochę się wstydzę, to jednak go tu prezentuję.

Efekt końcowy (dla 10000 elementów) wygląda tak:

Dla 50000 - tak:

(przeskalowane 50% w dół)

A tu 230000 (więcej zasadniczo nie ma sensu, bo się nie mieści na ekranie):

13 komentarzy

    1. Skopiować cały kod VBA do nowego modułu (drzewko po lewej jak zrobisz alt f11) potem ctrl – g i wpisujesz: ulam1 “AA50”, 1000

      1. Przyznam się nie znałem tego sposobu uruchamiania makra. Dlatego chętnie tu zaglądam bo zawsze się czegoś nauczę 🙂

  1. A teraz przyznam się, że jestem dość leniwy i nie chciało mi się czekać aż napiszesz ten kod i sam sobie napisałem :). Ja zrobiłem to tak, że w pętli sprawdzam czy są wypełnione komórki np. po lewej. Jeżeli coś tam jest to idzie w górę. Jak napotka pustą to sprawdza czy pod nią coś jest – jeżeli jest to idzie w lewo itp. Co do sprawdzania liczb pierwszych to tylko sprawdzam czy dzieli się bez reszty przez liczby od 2 do pierwiastka ze sprawdzanej liczby.

    Jeżeli chodzi o samą spiralę to moją uwagę zwróciły te puste (bez liczb pierwszych) kolumny. Najpierw zauważyłem te dwie kolumny w dół taka autostrada. Potem jak się przyjrzałem to zauważyłem że w lewo i do góry również są drogi (po jednej kolumnie/wierszu) i kolejna autostrada w prawo.

    Przeprowadziłem nawet eksperyment i z tej autostrady w dół robiłem macierz dodając jedną kolumnę do drugiej i zaznaczyłem na niej liczby pierwsze. Wyszło to:

    1. Ja z kolei zaobserwowałem, że wystarczy zwiększać długość krawędzi o jeden co dwa zakręty. Pewnie mógłbym też jakoś bardziej elegancko napisać samą zakrętologię, ale mi się nie chciało 😉 Co do sprawdzania pierwszości, liczby są tu na tyle niewielkie, że nie ma sensu jakakolwiek optymalizacja. Twoje podejście jest więc wystarczająco dobre…

      Podobną spiralę można też narysować na bazie sześciokąta lub spirali Archimedesa, tylko wtedy wyliczenie kolejnej kropki jest o tyle utrudnione, że trzeba z każdym krokiem wyznaczyć współrzędne na podstawie długości jednostkowej, co pod skosem (lub po krzywej) nie jest całkiem banalne.

      Pojawianie się ukośnych “autostrad” liczb pierwszych na spirali Ulama jest dość dobrze opisane na Wiki: https://en.wikipedia.org/wiki/Ulam_spiral#Explanation

      1. Też na to wpadłem (ale już po napisaniu pierwszego makra) i przymierzam się do napisania tego w ten sposób tak dla ćwiczenia. 🙂

        Co do autostrad to mnie bardziej ciekawią te bez liczb pierwszych. Te skośne z prajmami można tłumaczyć tym, że są na nich po prostu liczby nieparzyste więc i szansa wystąpienia prajmów większa. Sam fakt, że układają się w większe skupiska jest ciekawy i nie wiem czy można go wytłumaczyć przypadkiem. Z drugiej strony to jak widziałeś na tej mojej macierzy układają się podobnie.

        Natomiast te puste mnie bardziej intrygują. Kto wie może tam leży tajemnica liczb pierwszych 🙂

        1. Tam na tej stronie Wikipedii piszą coś o trójmianach i parzystości. Może tędy droga? Jeśli chodzi o rozwiązanie tajemnicy liczb pierwszych za pomocą spirali Ulama to jestem raczej konserwatywnie nastawiony. Nie tacy już próbowali 😉

          1. No właśnie tylu już próbowało i przez tyle lat nic. Wychodzi na to, że nie znamy jeszcze działań matematycznych, które potrafią opisać liczby pierwsze 🙂 . Jeżeli więc nie matematyka to może grafika, i to być może wielowymiarowa. Znaleźć wzór, który będzie się powtarzał? A może poszukać w innych dziadzinach chemii, fizyce, muzyce itp.? Jaja by były jakby się okazało, że z liczb pierwszych można ułożyć niezły kawałek muzyczny 🙂 Taki przebój wszechczasów 🙂

  2. Ciekawostka, uwaga nigdzie jeszcze nie publikowana 😉

    Jeżeli spiralę Ulama “złożyć” na pół to pola z liczbami pierwszymi nie pokrywają się. Składać można z góry na dół albo w poprzek.
    Zrobiłem makro, które “składa/łamie” spiralę w ten sposób, że gdy pola z liczbami pierwszymi się pokryją to zaznacza je na czerwono. Widać to przy “złożeniu” spirali na 4.

    O ile się gdzieś nie rąbnąłem to “złożone” spirale wyglądają tak:

      1. Rzekła dziewica zobaczywszy prącie 🙂

        A tak poważniej to ciężko powiedzieć czy jest tego jakieś zastosowanie. Ale chyba pokazuje jasno, że jest jakaś logika w liczbach pierwszych, a skoro jest to można ją znaleźć. Tylko może trzeba spojrzeć pod innym kątem.

Leave a Comment

Twój adres e-mail nie zostanie opublikowany.