diff --git a/Solutions/solutions.zip b/Solutions/solutions.zip index 17ac756..5f891ab 100644 Binary files a/Solutions/solutions.zip and b/Solutions/solutions.zip differ diff --git a/kmeans.py b/kmeans.py new file mode 100644 index 0000000..445fa26 --- /dev/null +++ b/kmeans.py @@ -0,0 +1,42 @@ +import math + +from pyinclude.draw import * +from pyinclude.point import * + +K = 3 +POINTS = 100 +RANGE = 640 + + +if __name__ == "__main__": + points = [ Point() for i in range(POINTS) ] + centroids = [ Centroid() for i in range(K) ] + + maxIterations = 10 + + # set random point positions + # generate K initial points and then position the rest of the + # points around the initial K + assert( len(points) >= len(centroids) ) + for p in points[:len(centroids)]: + p.randomise( RANGE ) + + for i in range( len(centroids), len(points) ): + points[i].randomise( RANGE, points[i%len(centroids)] ) + + # set random starting positions for the centroids + for c in centroids: + c.randomise( RANGE ) + print( c ) + + # clustering iterations + for i in range( maxIterations ): + # kmeans + + # display centroid positions + print( f"Iteration {i}" ) + for c in centroids: + print( c ) + + draw( points, centroids, RANGE, RANGE, f"{i:0{int(math.log(maxIterations,10)+1)}d}_clusters.bmp" ) + diff --git a/pyinclude/draw.py b/pyinclude/draw.py new file mode 100644 index 0000000..1374876 --- /dev/null +++ b/pyinclude/draw.py @@ -0,0 +1,50 @@ +from PIL import Image, ImageColor, ImageDraw + +__colormap = ( + (255, 0, 0), (255, 31, 0), (255, 63, 0), (255, 95, 0), (255, 127, 0), + (255, 159, 0), (255, 191, 0), (255, 223, 0), (255, 255, 0), (223, 255, 0), + (191, 255, 0), (159, 255, 0), (127, 255, 0), ( 95, 255, 0), ( 63, 255, 0), + ( 31, 255, 0), ( 0, 255, 0), ( 0, 255, 31), ( 0, 255, 63), ( 0, 255, 95), + ( 0, 255, 127), ( 0, 255, 159), ( 0, 255, 191), ( 0, 255, 223), ( 0, 255, 255), + ( 0, 223, 255), ( 0, 191, 255), ( 0, 159, 255), ( 0, 127, 255), ( 0, 95, 255), + ( 0, 63, 255), ( 0, 31, 255), ( 0, 0, 255), ( 31, 0, 255), ( 63, 0, 255), + ( 95, 0, 255), (127, 0, 255), (159, 0, 255), (191, 0, 255), (223, 0, 255), + (255, 0, 255), (255, 0, 223), (255, 0, 191), (255, 0, 159), (255, 0, 127), + (255, 0, 95), (255, 0, 63), (255, 0, 31), (255, 255, 255), ( 0, 0, 0) +) + +def draw( points, centroids, width=0, height=0, filename="temp.bmp" ): + _width = 0 + _height = 0 + clusters = 0 + + # find width, heigth based on max x, y of points, centroids + for p in points + centroids: + if p.cluster > clusters: clusters = p.cluster + if p.x > _width: _width = p.x + if p.y > _height: _height = p.y + + clusters += 1 + _width += 1 + _height += 1 + + # use out values if user hasn't specified any dimensions + if width==0 or height==0: + width = _width + height = _height + + # create the image + image = Image.new( "RGB", (width,height), (255,255,255) ) + draw = ImageDraw.Draw( image ) + + colors = [ __colormap[i] for i in range( 0, len(__colormap), int(len(__colormap)/clusters)) ] + + # draw the points + for p in points: + draw.ellipse( (p.x-2, p.y-2, p.x+2, p.y+2), fill=colors[p.cluster] ) + + # draw the clusters + for c in centroids: + draw.ellipse( (c.x-5, c.y-5, c.x+5, c.y+5), outline=colors[c.cluster] ) + + image.save( filename ) diff --git a/pyinclude/point.py b/pyinclude/point.py new file mode 100644 index 0000000..0c58598 --- /dev/null +++ b/pyinclude/point.py @@ -0,0 +1,41 @@ +import random + +class Coord: + def __init__( self, x=0, y=0 ): + self.x = int(x) + self.y = int(y) + + def __eq__( self, other ): + return self.x == other.x and self.y == other.y + + def randomise( self, range, coord=None ): + if coord is None: + self.x = random.randint( 0, range ) + self.y = random.randint( 0, range ) + return + + x = random.gauss( coord.x, range/10 ) + y = random.gauss( coord.y, range/10 ) + + if x < 0 : x = 0 + if y < 0 : y = 0 + if x > range : x = range + if y > range : y = range + + self.x = int(x) + self.y = int(y) + +class Point( Coord ): + def __init__( self, x=0, y=0, cluster=0 ): + Coord.__init__( self, x, y ) + self.cluster = cluster + +class Centroid( Coord ): + __counter = 0 + + def __init__( self, x=0, y=0 ): + self.cluster = Centroid.__counter + Centroid.__counter += 1 + + def __repr__( self ): + return f"Centroid[{self.cluster}] @ ({self.x},{self.y})" \ No newline at end of file