1 jan. 2015

Lorenz-attraktionen

Ni som sett filmen Jurassic Park, kan ha hört Jeff Goldblums karaktär Dr. Ian Malcom prata om strange attractions (kaotiska attraktorer). Tillåt mig bjuda på ett lite mer tekniskt nyårsäventyr i form av utforskandet av en sådan, nämligen en Lorenz-attraktion! Formeln är framtagen på 60-talet av Edward Norton Lorenz i syfte att beskriva oförutsägbara beteenden hos vädersystem. Hans arbete utgör grunden för modern kaosteori, vilket innebär att det är tack vare Lorenz vi kan beskriva rörelser vars fulla orsaker vi ännu inte förstår. Formeln beskrivs nedan i kod som kan köras på en modern PC, och genom att skriva in den, kan du både undersöka formeln och manipulera dess parametrar för att se vilket utslag det ger.

Om du har Windows 8.1 kan du testa följande kod utan att behöva ladda hem något, men du måste ge dig själv rätten att köra scriptfiler på datorn, vilket kräver att du är lokal administratör. Börja med att trycka på Start-knappen och skriv Windows PowerShell. Nu borde du se en post som heter just så i resultatlistan.


När du ser Windows PowerShell, högerklicka på den och välj Kör som administratör.


Windows kommer nu att fråga dig om du tillåter att Windows PowerShell ändrar på den här datorn, vilket du ska acceptera genom att klicka Ja. Och själva förändringen gör du genom att skriva följande kommando:

Set-ExecutionPolicy Unrestricted

Windows svarar genom att förklara vilken förändring som kommer att ske, och fråga dig om du vill genomföra den. Bekräfta genom att trycka y och därefter Enter.


Du har nu ändrat exekveringspolicyn i Windows, så att du fritt kan exekvera scripts. Om du skulle råka stänga Windows PowerShell, kan du öppna den igen genom att trycka Start och skriva Windows PowerShell och trycka Enter. Du behöver inte längre vara administratör.

Vi kan nu använda programmet Anteckningar för att skapa en scriptfil. Starta Anteckningar och skriv:

#Lorenz-attraktion

Korsbryggan betyder att raden är en kommentar, och alltså inte en rad som har någon teknisk betydelse. Du ska nu spara filen, och då är det viktigt att först tänka på var du sparar filen. Du måste kunna referera till filen från Windows PowerShell. Därefter är det viktigt att tänka på filändelsen. Anteckningar antar att du vill använda filändelsen .txt, men du måste ändra filformatet till Alla filer, och sedan välja ett filnamn med filändelsen .ps1, då .ps1 är den filändelse som används för PowerShell-scripts. Jag sparar min fil med följande namn och sökväg:

C:\Temp\lorenz.ps1

Nu kan vi växla tillbaka till Anteckningar och fortsätta att skriva kod. Efter raden som redan står där, ska vi komplettera med kod som läser in kodbiblioteket som ger tillgång till Windows grafiska användargränssnitt. På nästa rad, skriv följande kod (utan radbryte i den nya raden):

[System.Reflection.Assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

Denna kod talar om för systemet att vi vill läsa in den DLL-fil som ger oss tillgång till det grafiska användargränssnittet. Hur kan man veta vad man ska skriva? Jo, alla tillgängliga Windows-komponenter finns listade i den vanliga filutforskaren. Om du vill titta, navigera till sökvägen C:\Windows\assembly. Där presenteras helt enkelt en lista med tillgängliga kodbibliotek. I Microsoft-världen refererar man till dessa som assemblies.


Kodraden som läser in kodbiblioteket måste stå ensam på en rad, men framöver spelar radbryten ingen roll. Det går utmärkt att infoga radbryten, om så önskas. Om du är osäker på var du har dina radbryten kan du stänga av det automatiska radbrytet i Anteckningar från menyn Format.

Nu behöver vi ha en instans av ett fönster, så att vi får ett konkret fönster att arbeta med. För att åstadkomma detta, skriv följande kodrad:

[System.Windows.Forms.Form]$window = New-Object -TypeName "System.Windows.Forms.Form";

Nu kan vi använda namnet $window när helst vi vill göra något med vårat fönster. Skulle vi i detta läge köra programmet, skulle vi inte se något. Vi behöver ytterligare lite kod som talar om för Windows att fönstret ska visas, och hur stort det ska vara när det visas. Jag väljer att även kommentera koden.

#Visa fönstret.
$window.Width = 550;
$window.Height = 450;
$window.ShowDialog();

Nu kan vi testköra programmet. Även om det inte gör speciellt mycket än, så har vi ett synligt resultat. Spara scriptfilen, och skriv följande i Windows PowerShell (möjligtvis beroende på var du sparat din fil, och under vilket namn):

C:\Temp\lorenz.ps1

Nu borde du se ett tomt fönster. Bryt programmet genom att helt enkelt stänga fönstret med stängningskrysset uppe till höger.

Själva beskrivningen av Lorenz-attraktionen ska vi skicka till fönstret innan vi visar det. Strax före kommentaren #Visa fönstret skriver vi kod som berättar för Windows att vi tänker tillhandahålla en utseendebeskrivning. Jag passar även på att erhålla en referens till Windows programmeringsgränssnitt för att rita vektorgrafik. Själva Lorenz-attraktionen består av punkter, så det är mycket lämpligare att rita den med bitmapsgrafik, men vi behöver ändå ha tillgång till gränssnittet för vektorgrafik för att kunna presentera våran bitmapsbild på skärmen.

#Beskriv fönstrets utseende.
$window.Add_Paint({
    [System.Drawing.Graphics]$g = $_.Graphics;

});

Notera att jag har lämnat en tom rad i koden. Resterande kod ska infogas där.

För att testa gränssnittet kan du komplettera med följande rad (och kommentar, om så önskas) som helt enkelt ritar en diagonal linje i fönstret.

#Rita en linje - bara för att testa.
$g.DrawLine([System.Drawing.Pens]::Black, 0, 0, 550, 450);

Spara, och testkör!


Radera kodraden som ritar linjen, med tillhörande kommentar. Infoga istället följande kod som skapar bitmapsbilden och definierar färgen som vi ska använda för att rita:

#Skapa en bitmapsbild.
[System.Drawing.Bitmap]$b = New-Object -TypeName "System.Drawing.Bitmap" -ArgumentList (550, 450);

#Definiera färgen svart.
[System.Drawing.Color]$svart = [System.Drawing.Color]::FromArgb(0, 0, 0);

Därefter, skriv in koden som ritar kurvan.

#Initiala värden. Startposition.
[double]$x = 0.1; [double]$y = 0;
[double]$z = 0; [double]$t = 0;
    
#Iterera.
for([int]$i = 0; $i -le 10000; $i++)
{
    [double]$x1 = $x + 0.01 * 10 * ($y - $x);
    [double]$y1 = $y + 0.01 * ($x * (28 - $z) - $y);
    [double]$z1 = $z + 0.01 * ($x * $y - (8 / 3) * $z);
    $x = $x1;
    $y = $y1;
    $z = $z1;

    #Konvertera från 3D till 2D.
    [int]$phys_x = (275 + 10 * $x);
    [int]$phys_y = (25 + 6 * $z);

    #Placera ut en pixel på bitmapsbilden.
    $b.SetPixel($phys_x, $phys_y, $svart);
}

Avslutningsvis, komplettera med denna kod som presenterar bilden och förhindrar att programmet läcker minne:

#Rita bilden på fönstret.
$g.DrawImage($b, 0, 0);

#Radera bitmapsbilden för att frigöra minne.
$b.Dispose();

Spara och testkör! Du ska nu se en Lorens-attraktion på skärmen!


Om du vill återsälla exekveringspolicyn måste du starta Windows PowerShell som administratör, och skriva:

Set-ExecutionPolicy Restricted

Även denna gång måste du bekräfta genom att skriva y. För att bekräfta att förändringen verkligen är återställd, skriv:

Get-ExecutionPolicy

Mycket nöje! Så här ser den kompletta programlistningen ut. Tänk på att första programsatsen (rad nummer två) inte får innehålla något enter-slag.

#Lorenz-attraktion
[System.Reflection.Assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
[System.Windows.Forms.Form]$window = New-Object -TypeName "System.Windows.Forms.Form";

#Beskriv fönstrets utseende.
$window.Add_Paint({
    [System.Drawing.Graphics]$g = $_.Graphics;

    #Skapa en bitmapsbild.
    [System.Drawing.Bitmap]$b = New-Object -TypeName "System.Drawing.Bitmap" -ArgumentList (550, 450);

    #Definiera färgen svart.
    [System.Drawing.Color]$svart = [System.Drawing.Color]::FromArgb(0, 0, 0);

    #Initiala värden. Startposition.
    [double]$x = 0.1; [double]$y = 0;
    [double]$z = 0; [double]$t = 0;
    
    #Iterera.
    for([int]$i = 0; $i -le 10000; $i++)
    {
        [double]$x1 = $x + 0.01 * 10 * ($y - $x);
        [double]$y1 = $y + 0.01 * ($x * (28 - $z) - $y);
        [double]$z1 = $z + 0.01 * ($x * $y - (8 / 3) * $z);
        $x = $x1;
        $y = $y1;
        $z = $z1;

        #Konvertera från 3D till 2D.
        [int]$phys_x = (275 + 10 * $x);
        [int]$phys_y = (25 + 6 * $z);

        #Placera ut en pixel på bitmapsbilden.
        $b.SetPixel($phys_x, $phys_y, $svart);
    }

    #Rita bilden på fönstret.
    $g.DrawImage($b, 0, 0);

    #Radera bitmapsbilden för att frigöra minne.
    $b.Dispose();
});

#Visa fönstret.
$window.Width = 550;
$window.Height = 450;
$window.ShowDialog();

Dr. Ian Malcom nämnde även fjärilseffekten i filmen Jurassic Park, som även den har Lorenz som upphovsman.

4 kommentarer:

Lennart W sa...

Tack Anders! Det här ska jag verkligen försöka. Enda abret är att min egen laptop har Windows 7, men det kanske går ändå med inte så stora ändringar. Eller också testar jag med fruns eller dotterns som har w 8.. (Fast det är väl inte så att något av kommandona här låser upp datorn även för andra ev skadliga koder från t ex nätet? Möjligen en dum fråga...)

En språklig detalj är att den heter Lorenz-attraktorn på svenska. Inte Lorenz-attraktionen... Sen finns det iofs redan en del bra program som man kan rita t ex såna där kurvor i. Då tänker jag ffa på Maxima som är gratis. Fast det tar förstås också tid att lära sig hantera. (Sen tycker jag iofs att även MS Office Excel [eller Open Office Draw...] kan vara väldigt användbart för Quick and dirty - lösningar.)

Anders Hesselbom sa...

Lennart, Windows 8.1 och Windows 7 delar (trots sina visuella olikheter) applikationsplattform. Den enda skillnaden är faktiskt att Win 7 har sin sökruta i startmenyn, medan Win 8.1 har sin sökruta på just startskärmen. Det kommer att se olika ut, men allt kommer att fungera!

Lennart W sa...

Ok. Tack igen.
(Och så var det förstås Open Office Calc som avsågs som alternativ till Excel. My bad.)

Anders Hesselbom sa...

Tack själv! Detta är "Native Windows" och är helt oberoende av Office. Den gode Jonas Elfström hedrade mig faktiskt med att porta detta mitt lilla Windows-program till iPhone och Swift, och öppnade ett GitHub-Repository för saken.

 
Religion Blogg listad på Bloggtoppen.se