Merge branch 'master' of ssh://daknuett.eu:10022/daknuett/scientific-programming-exercises

This commit is contained in:
Daniel Knüttel 2019-02-25 13:08:19 +01:00
commit 8679f25e82
21 changed files with 13335 additions and 0 deletions

View File

@ -0,0 +1,36 @@
class FiniteStateMachine(object):
def __init__(self, start: int, valid: list, default: int, transitions: dict):
self._start = start
self._valid = valid
self._transitions = dict()
for state, trans in transitions.items():
self._transitions[state] = dict()
for words, target in trans.items():
for word in words:
self._transitions[state][word] = target
self._default = default
self._state = start
def reset(self):
self._state = self._start
def make_transition(self, word):
if(not self._state in self._transitions):
self._state = self._default
if(not word in self._transitions[self._state]):
self._state = self._default
return
self._state = self._transitions[self._state][word]
def check(self, sequence):
for word in sequence:
self.make_transition(word)
is_valid = self._state in self._valid
self.reset()
return is_valid

16
exam/ex06/main.py Normal file
View File

@ -0,0 +1,16 @@
from io import StringIO
from parser import Parser
from tokenio import TokenStream
from tokens import NumberTokenParser
texts = ["one plus one"
, "one plus two"
, "thirtytwo plus eleven"
, "four times four"
, "(eight plus eleven) times two"
, "twohundred through eleven"]
for text in texts:
print(text, "=", Parser(TokenStream(StringIO(text))).parse())

91
exam/ex06/parser.py Normal file
View File

@ -0,0 +1,91 @@
from collections import deque
from tokens import NumberTokenParser, OperatorTokenParser
class ParsingException(Exception):
pass
class Parser(object):
def __init__(self, token_stream):
self._token_stream = token_stream
self._stack = deque()
self._current_list = deque()
def parse(self):
state = 0
while True:
token = self._token_stream.get_token()
if(token == "("):
if(state == 1):
raise ParsingException(
"expected operator, not parenthesis: {} (near '{}')".format(
self._token_stream._offset
, token))
self._stack.append(self._current_list)
continue
if(NumberTokenParser.can_parse(token)):
if(state == 1):
raise ParsingException(
"expected operator, not number: {} (near '{}')".format(
self._token_stream._offset
, token))
self._current_list.append(NumberTokenParser(token).parse())
state = 1
continue
if(OperatorTokenParser.can_parse(token)):
if(state != 1):
raise ParsingException(
"expected number or parenthesis, not operator: {} (near '{}')".format(
self._token_stream._offset
, token))
self._current_list.append(OperatorTokenParser(token).parse())
state = 0
continue
if(token == ")"):
#if(state == 1):
# raise ParsingException(
# "expected operator, not parenthesis: {} (near '{}')".format(
# self._token_stream._offset
# , token))
state = 1
result = self.execute_branch(self._current_list)
self._current_list = self._stack.pop()
continue
if(not token):
if(self._stack):
raise ParsingException("unexpected EOF while parsing")
return self.execute_branch(self._current_list)
raise ParsingException("unknown token: {} (near '{}')".format(self._token_stream._offset, token))
return self.execute_branch(self._current_list)
def execute_branch(self, branch):
result = None
current_operator = None
for element in branch:
if(result is None):
result = element
continue
if(not isinstance(element, (float, int, complex))):
# Operator
current_operator = element
continue
if(current_operator):
result = current_operator(result, element)
current_operator = None
return result

56
exam/ex06/tokenio.py Normal file
View File

@ -0,0 +1,56 @@
from collections import deque
base_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
end_of_token_chars = "() \t"
whitespace_chars = " \t"
class UnexpectedCharacterException(Exception):
def __init__(self, msg, offset, char, *args):
Exception.__init__(self, *args)
self._msg = msg
self._offset = offset
self._char = char
class TokenStream(object):
def __init__(self, file_):
self._file = file_
self._file.seek(0, 0)
self._offset = 0
def _getc(self):
c = self._file.read(1)
if(c):
self._offset += 1
return c
def _ungetc(self):
self._file.seek(self._offset - 1, 0)
self._offset -= 1
def get_token(self):
result = deque()
while True:
c = self._getc()
if(not c):
# EOF.
break
if(c in base_chars):
result.append(c)
continue
if(c in end_of_token_chars):
if(not result):
# We are not inside a token.
if(c in whitespace_chars):
# Some whitespace. Ignore it.
continue
# A parenthesis.
return c
# End of token.
self._ungetc()
break
raise UnexpectedCharacterException("Unexpected character while tokenizing", self._offset, c)
return "".join(result)

155
exam/ex06/tokens.py Normal file
View File

@ -0,0 +1,155 @@
from collections import deque
from abc import ABCMeta, abstractmethod
from finite_state_machine import FiniteStateMachine
BASE_NUMBER_TOKENS = {"one": 1
, "two": 2
, "three": 3
, "four": 4
, "five": 5
, "six": 6
, "seven": 7
, "eight": 8
, "nine": 9}
DECI_NUMBER_TOKENS = {"twenty": 20
, "thirty": 30
, "fourty": 40
, "fifty": 50
, "sixty": 60
, "secenty": 70
, "eigthy": 80
, "ninety": 90}
TEEN_NUMBER_TOKENS = {"ten": 10
, "eleven": 11
, "twelve": 12
, "thirteen": 13
, "fourteen": 14
, "fifteen": 15
, "sixteen": 16
, "seventeen": 17
, "eighteen": 18
, "nineteen": 19}
HUNDRED_NUMBER_TOKENS = {"hundred": 100}
ZERO_NUMBER_TOKENS = {"zero": 0
, "null": 0}
OPERATOR_TOKENS = { "plus": lambda x,y: x + y
, "minus": lambda x,y: x - y
, "times": lambda x,y: x * y
, "through": lambda x,y: x / y}
transitions = {
0: { tuple(ZERO_NUMBER_TOKENS) + tuple(TEEN_NUMBER_TOKENS): 1
, tuple(DECI_NUMBER_TOKENS): 2
, tuple(BASE_NUMBER_TOKENS): 3}
, 2: {tuple(BASE_NUMBER_TOKENS): 1}
, 3: {tuple(HUNDRED_NUMBER_TOKENS): 4}
, 4: {tuple(DECI_NUMBER_TOKENS): 2}
}
valid_states = [1, 2, 3, 4]
default_transition = -1
class TokenParsingException(Exception):
pass
class SubtokenizingException(TokenParsingException):
pass
class TokenParser(metaclass = ABCMeta):
@classmethod
def can_parse(cls, token):
try:
cls(token).parse()
return True
except TokenParsingException:
return False
@abstractmethod
def parse(self):
pass
class NumberTokenParser(TokenParser):
def __init__(self, token):
self._token = token.lower()
self._token_length = len(token)
self._finite_state_machine = FiniteStateMachine(0, valid_states, default_transition, transitions)
def get_token_of_class_or_none(self, offset, token_class):
for token in token_class:
if(len(token) + offset > self._token_length):
continue
if(self._token[offset: offset + len(token)] == token):
return token
return None
def get_next_token_part(self, offset):
token_classes = [ZERO_NUMBER_TOKENS
, HUNDRED_NUMBER_TOKENS
, TEEN_NUMBER_TOKENS
, DECI_NUMBER_TOKENS
, BASE_NUMBER_TOKENS]
result = None
for token_class in token_classes:
result = self.get_token_of_class_or_none(offset, token_class)
if(result):
break
return result
def subtokenize(self):
token_parts = deque()
offset = 0
while(True):
subtoken = self.get_next_token_part(offset)
if(subtoken is None):
if(offset != self._token_length):
raise SubtokenizingException("part of the token is dangling: '{}'".format(self._token[offset:]))
return list(token_parts)
offset += len(subtoken)
token_parts.append(subtoken)
def parse(self):
token_parts = self.subtokenize()
if(not self._finite_state_machine.check(token_parts)):
raise TokenParsingException("token '{}' is invalid".format(self._token))
# This is ugly but it works.
result = 0
for subtoken in token_parts:
if(subtoken in BASE_NUMBER_TOKENS):
result += BASE_NUMBER_TOKENS[subtoken]
if(subtoken in TEEN_NUMBER_TOKENS):
result += TEEN_NUMBER_TOKENS[subtoken]
if(subtoken in DECI_NUMBER_TOKENS):
result += DECI_NUMBER_TOKENS[subtoken]
if(subtoken in HUNDRED_NUMBER_TOKENS):
result *= HUNDRED_NUMBER_TOKENS[subtoken]
return result
class OperatorTokenParser(TokenParser):
def __init__(self, token):
self._token = token.lower()
def parse(self):
if(not self._token in OPERATOR_TOKENS):
raise TokenParsingException("token '{}' is not an operator".format(self._token))
return OPERATOR_TOKENS[self._token]

49
exam/ex08/main.py Normal file
View File

@ -0,0 +1,49 @@
import numpy as np
import matplotlib.pyplot as plt
class BrownIterator(object):
def __init__(self, N, m):
self._N = N
self._max_m = m
self._i = 0
self._xs = None
self._ys = None
def __iter__(self):
self._xs = np.zeros(self._N) + 1.5
self._ys = np.zeros(self._N)
self._i = 0
return self
def __next__(self):
self._i += 1
if(self._i > self._max_m):
raise StopIteration()
if(self._i == 1):
return self._xs, self._ys
theta = np.random.uniform(0, np.pi * 2, self._N)
self._xs = self._xs + np.cos(theta)
self._ys = self._ys + np.sin(theta)
# Reflect the particles
self._xs[self._xs > 3] += 1.5 * (3 - self._xs[self._xs > 3])
self._ys[self._ys > 3] += 1.5 * (3 - self._ys[self._ys > 3])
self._xs[self._xs < -3] += 1.5 * (-3 - self._xs[self._xs < -3])
self._ys[self._ys < -3] += 1.5 * (-3 - self._ys[self._ys < -3])
return self._xs, self._ys
if( __name__ == "__main__"):
data = np.array([i for i in BrownIterator(1000, 251)])
print(data)
p1, = plt.plot(data[5,0], data[5,1], "r.", label="t = 5")
p2, = plt.plot(data[25,0], data[25,1], "y.", label="t = 25")
p3, = plt.plot(data[50,0], data[50,1], "b.", label="t = 50")
p4, = plt.plot(data[250,0], data[250,1], "g.", label="t = 250")
plt.legend(handles=[p1, p2, p3, p4])
plt.show()

64
exam/ex10/main.py Normal file
View File

@ -0,0 +1,64 @@
import numpy as np
import matplotlib.pyplot as plt
class StochastikIterator(object):
def __init__(self, r, num_simulations, chi0, std_deviation, maxiter=None):
self._r = r
self._num_simulations = num_simulations
self._chi0 = chi0
self._std_deviation = std_deviation
self._maxiter=maxiter
self._chi = None
self._currentiter = None
def __iter__(self):
self._currentiter = 0
self._chi = np.array([self._chi0] * self._num_simulations, dtype=np.float64)
return self
def __next__(self):
if(self._maxiter and self._currentiter == self._maxiter):
raise StopIteration()
self._currentiter += 1
result = self._chi.copy()
self._chi *= np.exp(r - np.random.normal(scale=self._std_deviation, size=self._num_simulations))
return result
def do(self, iters=None):
if(not (iters or self._maxiter)):
raise ValueError("iters or maxiter must be specified")
if(iters):
until = iters
if(self._maxiter and (not iters or (iters and self._maxiter < iters))):
until = self._maxiter
self._currentiter = 0
self._chi = np.array([self._chi0] * self._num_simulations, dtype=np.float64)
for i in range(until):
self._chi *= np.exp(r - np.random.normal(scale=self._std_deviation, size=self._num_simulations))
return self._chi.copy()
mean_chi = lambda r: np.mean(StochastikIterator(r, 500, 100, 0.25).do(70))
for r in [0.05, 0, -0.05]:
print("r =", r, "\t<chi> = ", mean_chi(r))
R = np.arange(-4, 4, 0.01)
M = [mean_chi(r) for r in R]
plt.plot(R, M, label="$<\\chi>$")
plt.show()

67
exam/ex13/backend.py Normal file
View File

@ -0,0 +1,67 @@
import numpy as np
CELL_IS_ALIVE = 0b1
CELL_WILL_BE_ALIVE = 0b10
CELL_WILL_BE_DEAD = 0b100
def is_alive(model, i, j):
return model[i][j] & CELL_IS_ALIVE
def get_cells_around(model, i, j):
# Most common case. Handle this first.
# Fortunately the most common case is also the fastets to handle.
if(i > 2 and j > 2 and i < model.shape[0] and j < model.shape[1]):
return model[i - 2: i + 2, j - 2: j + 2]
# Neither negative indices nor indices that overflow
# are handled properly by numpy. So basically I have to do that manually:
cells_around = np.zeros([3, 3], np.int8)
for k, m in enumerate(range(i - 1, i + 2)):
for l, n in enumerate(range(j - 1, j + 2)):
real_m = m
if(m >= model.shape[0]):
real_m = m - model.shape[0]
if(m < 0):
real_m = m + model.shape[0]
real_n = n
if(n >= model.shape[1]):
real_n = n - model.shape[1]
if(n < 0):
real_n = n + model.shape[1]
cells_around[k][l] = model[real_m][real_n]
return cells_around
def count_living_cells_around(model, i, j):
cells_around = get_cells_around(model, i, j)
living_cells_around = (cells_around & CELL_IS_ALIVE) == 1
living_cells_around[1][1] = False
unique, counts = np.unique(living_cells_around, return_counts=True)
if(True not in unique):
return 0
return counts[np.where(unique == True)[0]]
def handle_cell(model, i, j):
living_cells_around = count_living_cells_around(model, i, j)
if(not is_alive(model, i, j)):
if(living_cells_around == 3):
model[i][j] = CELL_WILL_BE_ALIVE
return
if(living_cells_around > 3 or living_cells_around < 2):
model[i][j] = CELL_WILL_BE_DEAD
def after_tick_finished(model):
# kill the cells that have died
model[(model & CELL_WILL_BE_DEAD) != 0] = 0
# create the new cells
model[(model & CELL_WILL_BE_ALIVE) != 0] = 1
def total_living_cells(model):
living_cells = (model & CELL_IS_ALIVE) == 1
unique, counts = np.unique(living_cells, return_counts=True)
if(True not in unique):
return 0
return counts[np.where(unique == True)[0]]

11
exam/ex13/executor.py Normal file
View File

@ -0,0 +1,11 @@
from backend import handle_cell, after_tick_finished
def execute_tick(model):
n, m = model.shape
for i in range(n):
for j in range(m):
handle_cell(model, i, j)
after_tick_finished(model)

28
exam/ex13/main.py Normal file
View File

@ -0,0 +1,28 @@
import matplotlib.pyplot as plt
import numpy as np
from model import prepare_model
from backend import total_living_cells
from executor import execute_tick
def cells_alive_at_ticks(model, ticks):
for i in range(ticks):
cell_count = total_living_cells(model)
yield [i, cell_count]
execute_tick(model)
plot_data = {
0.3 : ("r", "p=0.3")
, 0.5: ("g", "p=0.5")
, 0.7: ("b", "p=0.7")
}
handles = []
for p, (color, label) in plot_data.items():
model = prepare_model(100, p)
living_cells = np.array(list(cells_alive_at_ticks(model, 10)))
plot, = plt.plot(living_cells[:, 0], living_cells[:, 1], color, label=label)
handles.append(plot)
plt.legend(handles=handles)
plt.show()

8
exam/ex13/model.py Normal file
View File

@ -0,0 +1,8 @@
import numpy as np
def prepare_model(n, p):
"""
p must be between 0 and 1.
"""
return (np.random.rand(n, n) < p).astype(np.int8)

34
exam/ex14/main.py Normal file
View File

@ -0,0 +1,34 @@
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import numpy as np
import sys
sys.path.append("../ex13/")
from model import prepare_model
from backend import CELL_IS_ALIVE
from executor import execute_tick
fig = plt.figure(figsize=(7, 7))
ax = fig.add_axes([0, 0, 1, 1], frameon=False)
ax.set_xlim(0, 100)
ax.set_xticks([])
ax.set_ylim(0, 100)
ax.set_yticks([])
model = prepare_model(100, 0.2)
scat, = ax.plot(*np.where((model & CELL_IS_ALIVE) == 1), "s", color="black")
frames = 100
def update(i):
execute_tick(model)
scat.set_data(*np.where((model & CELL_IS_ALIVE) == 1))
print("%.2f" % ((i / frames) * 100), "%", end="\r")
animation = ani.FuncAnimation(fig, update, range(frames), interval=1)
animation.save("output/animation.gif", dpi=80, writer='imagemagick')
print("\noutput/animation.gif")

122
exam/ex16/geometry.py Normal file
View File

@ -0,0 +1,122 @@
from abc import abstractmethod, ABCMeta
from itertools import permutations
import numpy as np
class Point(object):
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
def __abs__(self):
return (self.x**2 + self.y**2)**0.5
def __add__(self, other):
if(isinstance(self, Point)):
return Point(self.x + other.x, self.y + other.y)
raise TypeError("cannot add {} and {}".format(type(other), type(self)))
def __sub__(self, other):
if(isinstance(self, Point)):
return Point(self.x - other.x, self.y - other.y)
raise TypeError("cannot subtract {} and {}".format(type(other), type(self)))
def rotate(self, angle):
return Point(self.x * np.cos(angle) - self.y * np.sin(angle)
, self.x * np.sin(angle) + self.y * np.cos(angle))
def __repr__(self):
return "{}({}, {})".format(type(self).__name__, self.x, self.y)
class TwoDimensionalObject(metaclass=ABCMeta):
@abstractmethod
def contains_point(self, point: Point):
pass
def __contains__(self, other):
if(isinstance(other, Point)):
return self.contains_point(other)
raise TypeError("unable to check if {} is in {}".format(type(other), type(self)))
class Circle(TwoDimensionalObject):
def __init__(self, origin: Point, radius):
self.origin = origin
self.radius = radius
def contains_point(self, point: Point):
return abs(self.origin - point) < self.radius
def __repr__(self):
return "{}({}, {})".format(type(self).__name__, self.origin, self.radius)
class Rectangle(TwoDimensionalObject):
"""
A Rectangle is constructed as follows:
The Points p1, p2 are connected using orthogonal lines::
p1 +-------+
| |
| |
| |
+-------+ p2
and then the Rectangle is rotated ``angle`` degrees around ``p1``.
"""
def __init__(self, p1: Point, p2: Point, angle=0):
self.p1 = p1
self.p2 = p2
self.local_p1 = Point(0, 0)
self.local_p2 = (p2 - p1).rotate(angle)
self.angle = angle
def contains_point(self, point: Point):
point_in_eigen_frame = (point - self.p1).rotate(self.angle)
if(self.local_p1.x < self.local_p2.x):
if(point_in_eigen_frame.x < self.local_p1.x or point_in_eigen_frame.x > self.local_p1.x):
return False
else:
if(point_in_eigen_frame.x > self.local_p1.x or point_in_eigen_frame.x < self.local_p1.x):
return False
if(self.local_p1.y < self.local_p2.y):
if(point_in_eigen_frame.y < self.local_p1.y or point_in_eigen_frame.y > self.local_p1.y):
return False
else:
if(point_in_eigen_frame.y > self.local_p1.y or point_in_eigen_frame.y < self.local_p1.y):
return False
return True
def __repr__(self):
return "{}({}, {}, angle={})".format(type(self).__name__, self.p1, self.p2, self.angle)
class Triangle(TwoDimensionalObject):
def __init__(self, p1: Point, p2: Point, p3: Point):
self.p1 = p1
self.p2 = p2
self.p3 = p3
def contains_point(self, point: Point):
points = [self.p1, self.p2, self.p3]
triangle_surface = abs(sum([points[i].x * (points[(i + 1) % 3].y - points[(i + 2) % 3].y) for i in range(3)]))
points = [point, self.p1, self.p2, self.p3]
surfaces = [abs(sum([p[i].x * (p[(i + 1) % 4].y - p[(i + 2) % 4].y) for i in range(4)])) for p in permutations(points)]
return max(surfaces) < triangle_surface
def __repr__(self):
return "{}({}, {}, {})".format(type(self).__name__, self.p1, self.p2, self.p3)
class CollectionOfFigures(object):
def __init__(self, figures):
self.figures = figures
def containing(self, point: Point):
return [f for f in self.figures if point in f]
def __repr__(self):
return "{}({})".format(type(self).__name__, repr(self.figures))

21
exam/ex16/main.py Normal file
View File

@ -0,0 +1,21 @@
from geometry import Point, Rectangle, Triangle, Circle, CollectionOfFigures
c1 = Circle(Point(0, 0), 1)
c2 = Circle(Point(4, 4), 2)
r1 = Rectangle(Point(0, 0), Point(4, 4))
r2 = Rectangle(Point(-3, -3), Point(-1, -1))
t1 = Triangle(Point(-1, -1), Point(-1, 1), Point(0, 1))
t2 = Triangle(Point(0, 0), Point(6, 0), Point(0, 6))
t3 = Triangle(Point(-5, -5), Point(0, 6), Point(5, -5))
collection = CollectionOfFigures([c1, c2, r1, r2, t1, t2, t3])
p1 = Point(4, 4)
p2 = Point(-1, 0)
p3 = Point(0, 0)
print(p1, "is in", collection.containing(p1))
print(p2, "is in", collection.containing(p2))
print(p3, "is in", collection.containing(p3))

32
exam/ex19/caesar.py Normal file
View File

@ -0,0 +1,32 @@
def internal_ord(c):
c = c.lower()
internal_ord = ord(c) - ord("a")
if(internal_ord < 0 or internal_ord > 25):
raise ValueError("'{}' is an unsupported character".format(c))
return internal_ord
def internal_chr(i):
return chr(ord("a") + i)
def encode_or_keeps_space(c):
if(c == " "):
return (False, c)
return (True, internal_ord(c))
def prepare_string(s):
return (encode_or_keeps_space(c) for c in s)
def _caesar(s, K):
for encode, i in prepare_string(s):
if(encode):
yield internal_chr((i + K) % 26)
else:
yield i
def caesar(s, K):
return "".join(_caesar(s, K))

21
exam/ex19/main.py Normal file
View File

@ -0,0 +1,21 @@
from caesar import caesar
from statistical_attack import get_statistical_key
text1 = "hello world this is a test"
text2 = "this is a message that is obfuscated"
K2 = 4
K1 = 13
print(caesar(text1, K1))
print(caesar(caesar(text1, K1), K1))
print(caesar(text2, K2))
print(caesar(caesar(text2, K2), abs(K2 - 26)))
text4 = "In cryptography, a Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on. The method is named after Julius Caesar, who used it in his private correspondence"
text4 = "".join((s for s in text4 if s in " abcdefghijklmnopqrstuvwxyz"))
print(get_statistical_key(caesar(text4, K2)))
print(get_statistical_key(caesar(text2, K2)))

12326
exam/ex19/sample.tx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
from collections import Counter
alphabet = "abcdefghijklmnopqrstuvwxyz"
reference = Counter((c for c in open("sample.tx").read().lower() if c in alphabet))
def get_statistics(text):
return Counter([c for c in text.lower() if c in alphabet])
def relative_most_common(statistics):
c, abs_probability = statistics.most_common(1)[0]
total_chrs = sum([v for k,v in statistics.most_common()])
return abs_probability / total_chrs
def get_statistical_key(text):
statistics = get_statistics(text)
quality = relative_most_common(statistics) / relative_most_common(reference)
c, abs_probability = statistics.most_common(1)[0]
K = abs(ord("e") - ord(c))
if(quality > 1):
quality = relative_most_common(reference) / relative_most_common(statistics)
return K, quality

13
exam/ex26/main.py Normal file
View File

@ -0,0 +1,13 @@
from polynomial import Polynomial
p1 = Polynomial([0, 2, 3, 4, 0, 0, 9])
p2 = Polynomial([0, 0, 1])
p3 = Polynomial([1, 0, 0, 1])
print("p1 = ", p1)
print("p2 = ", p2)
print("p3 = ", p3)
print("p1 + p3 = ", p1 + p3)
print("p2 * p3 = ", p2 * p3)
print("p1 * p3 = ", p1 * p3)

55
exam/ex26/polynomial.py Normal file
View File

@ -0,0 +1,55 @@
from collections import defaultdict
class Polynomial(object):
__slots__ = ["koefficients"]
def __init__(self, koefficients):
self.koefficients = list(koefficients)
# Remove trailing zeros.
while(self.koefficients and self.koefficients[-1] == 0):
self.koefficients.pop()
def __call__(self, x):
if(not isinstance(x, (float, int, complex))):
raise TypeError("unsupported type for {}: {}".format(x, type(x)))
result = 0
for i, a in enumerate(self.koefficients):
result += a * x**i
return result
def __add__(self, other):
if(not isinstance(other, Polynomial)):
raise TypeError("cannot add {} and {}".format(type(self), type(other)))
p1 = self.koefficients
p2 = other.koefficients
if(len(other.koefficients) > len(self.koefficients)):
p1, p2 = p2, p1
p2 += [0] * (len(p1) - len(p2))
return Polynomial([a + b for a,b in zip(p1, p2)])
def __mul__(self, other):
if(isinstance(other, (int, float, complex))):
return Polynomial([a * other for a in self.koefficients])
if(not isinstance(other, Polynomial)):
raise TypeError("cannot add {} and {}".format(type(self), type(other)))
result = defaultdict(int)
for i, k1 in enumerate(self.koefficients):
for j, k2 in enumerate(other.koefficients):
result[i + j] += k1 * k2
return Polynomial(result[i] for i in range(max(result)))
def __repr__(self):
return "{}({})".format(type(self).__name__, self.koefficients)
def __str__(self):
return " + ".join(["{}*x**{}".format(a, i) for i, a in enumerate(self.koefficients)])

101
exam/ex27/main.py Normal file
View File

@ -0,0 +1,101 @@
import sys
sys.path.append("../../")
import numpy as np
from collections import deque
import matplotlib.pyplot as plt
from util.io import readvalue
def positive_int(s):
i = int(s)
if(not i > 0):
raise ValueError("{} <= 0".format(i))
return i
def float_0_1(s):
i = float(s)
if(not i > 0):
raise ValueError("{} <= 0".format(i))
if(i > 1):
raise ValueError("{} > 1".format(i))
return i
IS_TREE = 1
TREE_IS_ON_FIRE = 2
TREE_WILL_BE_ON_FIRE = 4
def prepare_model(N, p):
model = np.zeros((N, N), dtype=np.int8)
model[np.random.uniform(size=(N,N)) < p] = IS_TREE
return model
def index_plus(i, j, add_i, add_j, model):
new_i = i + add_i
new_j = j + add_j
if(new_i >= model.shape[0]):
new_i -= model.shape[0]
if(new_i < 0):
new_i += model.shape[0]
if(new_j >= model.shape[1]):
new_j -= model.shape[1]
if(new_j < 0):
new_j += model.shape[1]
return new_i, new_j
def do_tick(model):
for i in range(model.shape[0]):
for j in range(model.shape[1]):
if((not model[i][j] & IS_TREE) or model[i][j] & TREE_IS_ON_FIRE):
continue
if(
(model[index_plus(i, j, 1, 0, model)] & TREE_IS_ON_FIRE)
or (model[index_plus(i, j, 0, 1, model)] & TREE_IS_ON_FIRE)
or (model[index_plus(i, j, -1, 0, model)] & TREE_IS_ON_FIRE)
or (model[index_plus(i, j, 0, -1, model)] & TREE_IS_ON_FIRE)
):
model[i][j] |= TREE_WILL_BE_ON_FIRE
model[(model & TREE_WILL_BE_ON_FIRE) != 0] |= TREE_IS_ON_FIRE
model[(model & TREE_WILL_BE_ON_FIRE) != 0] ^= TREE_WILL_BE_ON_FIRE
def trees_on_fire(model):
unique, counts = np.unique((model & TREE_IS_ON_FIRE) != 0, return_counts=True)
if(True not in unique):
return 0
return counts[np.where(unique == True)[0]][0]
def set_initial_on_fire(model):
i = np.random.randint(0, model.shape[0])
j = np.random.randint(0, model.shape[1])
while True:
if(model[i][j] & IS_TREE):
model[i][j] |= TREE_IS_ON_FIRE
break
i = (i + 1) % model.shape[0]
j = (j + 1) % model.shape[1]
N = readvalue("N > ", positive_int)
p = readvalue("p > ", float_0_1)
model = prepare_model(N, p)
set_initial_on_fire(model)
on_fire = deque()
last_on_fire = 1
while True:
do_tick(model)
now_on_fire = trees_on_fire(model)
on_fire.append(now_on_fire)
if(now_on_fire == last_on_fire):
break
last_on_fire = now_on_fire
plt.plot(*zip(*enumerate(on_fire)))
plt.show()