Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python Voronoin kiemurat

Sivun loppuun

pipo [21.05.2009 01:42:44]

#

Piirsin tommosen kuvan missä harmaansävyisen pikselin arvo = -1 * pikselin etäisyys lähimpään soluun + pikselin etäisyys toiseksi lähimpään soluun. Solut on pisteitä, mitkä on sijoiteltu lähes tasaisin välimatkoin.

Miten saisin viivat (kuvan tummimmat kohdat) kiemurtelemaan satunnaisesti? Tähän tyyliin.

Onko math.sqrt(x) yhtä nopea tai hidas kuin x**0.5?

# -*- coding: cp1252 -*-
import random

class Voronoi:
    def __init__(self, sivunpituus=100,solunsivunpituus=20):
        self.sivu = sivunpituus
        self.solunkoko = solunsivunpituus
        # solujen määrä per sivu
        self.soluja = self.sivu / self.solunkoko
        if (self.sivu % self.solunkoko) != 0:
            self.soluja += 1
        self.solut = self.luo_taulukko(self.soluja, self.soluja)
        self.arvo_pisteet()
        self.arvot = self.luo_taulukko(self.sivu, self.sivu)
        self.laske_arvot()
        print 'valmis.'

    def luo_taulukko(self,leveys=2,korkeus=2,arvo=0):
        '''Palauttaa tyhjän (leveys, korkeus)-kokoisen taulukon.'''
        t = []
        for i in range(leveys):
            t.append([])
            for j in range(korkeus):
                t[i].append(arvo)
        return t

    def arvo_pisteet(self):
        '''Täyttää solut-taulukon arvottujen pisteiden koordinaateilla.'''
        for i in range(0,self.soluja):
            for j in range(0,self.soluja):
                x = i * self.solunkoko
                x += random.randrange(0,self.solunkoko-1)
                y = j * self.solunkoko
                y += random.randrange(0,self.solunkoko-1)
                self.solut[i][j] = (x,y)

    def lahimmat_solut(self, x, y):
        '''Palauttaa tarkasteltavien solujen indeksit.'''
        lahimmat = []
        for j in range(3):
            for i in range(3):
                if ( x - 1 + i ) >= 0 and ( x - 1 + i ) < self.soluja:
                    if ( y - 1 + j ) >= 0 and ( y - 1 + j ) < self.soluja:
                        x1 = x - 1 + i
                        y1 = y - 1 + j
                        lahimmat.append((x1,y1))
        return lahimmat


    def etaisyys(self, a, b):
        return ((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2)**0.5

    def nopea_etaisyys(self, a, b):
        return (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2

    def lahimmat_etaisyydet(self, x, y):
        '''Palauttaa x,y :tä lähimpien solujen etäisyydet.'''
        etaisyydet = []
        solux = x / self.solunkoko
        soluy = y / self.solunkoko
        solut = self.lahimmat_solut(solux,soluy)
        for solu in range(len(solut)):
            solut[solu] = self.solut[ solut[solu][0] ] [ solut[solu][1] ]
        for solu in range(len(solut)):
            etaisyys = self.etaisyys((x,y),solut[solu])
            etaisyydet.append(etaisyys)
        etaisyydet.sort()
        return etaisyydet[0:3]

    def laske_arvot(self):
        '''Laskee jokaisen pisteen arvon.
        Arvo =    c[0] * etäisyys lähimpään soluun
                + c[1] * toiseksi lähimpään soluun'''
        c = [-1, 1, 0.5] # etäisyyksien kertoimet
        suurin = 0.0
        for y in range(self.sivu):
            for x in range(self.sivu):
                d = self.lahimmat_etaisyydet(x,y)
                try:
                    arvo = c[0]*d[0] + c[1]*d[1]
                    #+ c[2]*d[2] # ...c[n]*d[n]
                except:
                    print x,y,d
                self.arvot[x][y] = arvo
                if arvo > suurin:
                    suurin = arvo
        self.skaalaa(suurin)

    def skaalaa(self, suurin):
        '''Skaalataan kaikki arvot välille 0...1'''
        for y in range(self.sivu):
            for x in range(self.sivu):
                arvo = self.arvot[x][y]
                arvo = arvo / suurin
                self.arvot[x][y] = arvo

    def anna_arvot(self):
        return self.arvot

Metabolix [21.05.2009 12:44:50]

#

pipo kirjoitti:

Onko math.sqrt(x) yhtä nopea tai hidas kuin x**0.5?

En ole tässä erityisesti Pythoniin tutustunut, mutta x86-konekielessä on suoraan komento neliöjuurelle, kun taas yleisen potenssifunktion laskemiseen tarvitaan useampia operaatioita. Luultavasti siis sqrt toimii nopeammin. Voi tietenkin olla, että nämä käsitellään Pythonissa jotenkin eri tavalla ja suoraan luvuilla tehtävästä laskusta tuleekin nopeampi.

Antti Laaksonen [21.05.2009 16:56:17]

#

Tässä on pieni testiohjelma, joka laskee lukujen 1–9999999 neliöjuuret kaksi kertaa, ensin kaavalla n ** .5 ja sitten kaavalla math.sqrt(n). Minun koneellani nopeudet ovat vastaavasti 6,2 sekuntia ja 9,4 sekuntia, joten n ** .5 tuntuu olevan vähän nopeampi.

import math, time

aika = time.clock()
for n in xrange(1, 10000000):
    x = n ** .5
print time.clock() - aika

aika = time.clock()
for n in xrange(1, 10000000):
    x = math.sqrt(n)
print time.clock() - aika

Metabolix [21.05.2009 19:27:15]

#

Antti Laaksonen kirjoitti:

Minun koneellani nopeudet ovat vastaavasti 6,2 sekuntia ja 9,4 sekuntia, joten n ** .5 tuntuu olevan vähän nopeampi.

Mielenkiintoista: minulla koodisi antaa yleensä kummallekin suunnilleen saman ajan, mutta joillain lukujen määrillä (mm. 1000000) juuri n**0.5 vaikuttaisi olevan jopa kolmanneksen hitaampi.

pipo [21.05.2009 19:46:25]

#

Mulla x**0.5 nopeampi myös. 6,4 ja 10 sekuntia. Math.pow(n,x) oli kuitenkin nopeampi kuin n**x. Onkohan noitten tarkkuudessa jotain eroja.

Chiman [21.05.2009 21:49:47]

#

Kiemurteluun en keksi kätevää tapaa, mutta jos sopii, näytän pari vaihtoehtoista tapaa toteuttaa kaksi metodia:

def luo_taulukko(self, leveys=2, korkeus=2, arvo=0):
    '''Palauta tyhjä (leveys, korkeus) -kokoinen taulukko'''
    return [[arvo for y in range(korkeus)] for x in range(leveys)]

def lahimmat_solut(self, x, y):
    '''Palauta lähimpien solujen indeksit'''
    suunnat = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1)]
    def rajoissa(x1, y1):
        return 0 <= x1 < self.soluja and 0 <= y1 < self.soluja
    return [(x + dx, y + dy) for dx, dy in suunnat if rajoissa(x + dx, y + dy)]

Näissä on käytössä list comprehension (vakiintunutta suomenkielistä termiä ei taida olla).

pipo [21.05.2009 22:18:35]

#

Joo kiitos. Oon nähnyt list comprehensionin käyttöä kyllä, mutten ole ikinä vaivautunut syvetymään.

Alustavissa tutkimuksissa kiemurteluun löytyi ainakin yksi tapa, jonka myös herrat Ebert, Musgrave et al esittävät kirjassaan Texturing&Modeling: A Procedural Approach sivulla 91.

En vielä kerinnyt soveltamaan tota tekstuurin muokkausta kuva-/taulukkomuotoon, mutta varmaankin menisi siten, että (s, t) = solun koordinaatit ja (ss, tt) = koordinaatit solun sisällä.

Milo [25.05.2009 20:57:11]

#

Testasin tuota Laaksosen testisarjaa lisäämällä siihen math.pow() tarkistuksen mukaan ja sain tulokseksi:
6.59 (n**0.5)
12.11 (math.pow(n,0.5))
9.02 (math.sqrt(n))

Mutta ensimmäisessä testissä ei ole ollenkaan funktiokutsua, joten kokeilin toista tapaa:

import math, time

def oma_sqrt(x): return x ** .5

aika = time.clock()
for n in xrange(1, 10000000):
    x = oma_sqrt(n)
print time.clock() - aika

aika = time.clock()
for n in xrange(1, 10000000):
    x = math.pow(n,0.5)
print time.clock() - aika


aika = time.clock()
for n in xrange(1, 10000000):
    x = math.sqrt(n)
print time.clock() - aika

ja uudet tulokset tässä:
10.21
12.14
9.0

Ilmeisesti pythonissa funktiokutsuihin menee 50% enemmän aikaa.

Pilkki [15.06.2009 15:10:23]

#

Ainahan nopeutukseen voi käyttää psycoa, joa optimoi koodia. Käyttö on helppoa ja vaatii peräti kaksi riviä alkuun.

import psyco
psyco.full()

Ihan missään yksinkertaisimmissa tuo ei toki nopeuta, mutta heti kun koodia alkaa olemaan vähänkin enemmän, eron huomaa.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta