I have a simple formula taking basic arithmetic calculations given several inputs.
a = 1
b = 2
c = a + b #3=1+2
d = 4
e = c + d #7=3+4
In theory, the relationships should always hold true. And I want to write a function that user can modify any variable and the rest will be auto updated (update priority has been predefined if there are more than one alternative eg. update the most right node first).
def f():
#default state
state = {'a':1, 'b':2, 'c':3, 'd':4, 'e':7}
...
return state
f(a=0) == {'a':0, 'b':2, 'c':2, 'd':4, 'e':6}
f(c=4) == {'a':1, 'b':3, 'c':4, 'd':4, 'e':8}
f(b=2, c=4) == {'a':2, 'b':2, 'c':4, 'd':4, 'e':8}
I tried to use **kwargs and *args to allow the user to pass in any variable but have to hard code the update logic based on which variable got modified. Any better ideas?
P.S.: this example is for demonstration purpose; the real problem involves much more variables and the mathematical relationship is also more difficult (logarithm, exponential, ..)
You indeed can represent the state of the problem as a directional graph, where each edge between the variables contains an arithematic expression to use on the other node to update its state.
Than, use the BFS algorithem starting at the variable that you try to change and it will modify all of the varialbes that are linked to it and so forward.
If you have to change multiple varialbes, run the bfs multiple times, starting at each variable.
Than, if the one of the variable you ran the BFS on changes (and does not contain the wanted value), than you know the wanted state is not possible (because the variables are co-dependent).
So each time you add a state to the problem, you have to modify only the edges connected to it (and from it).
Problem Analysis
Two numbers get combined with a mathematical operators, resulting in another number
Resultant numbers depend on a relationship,
These relationships and numbers are illustrated as a tree structure.
Hence, there are two types of cells (#Copperfield):
Free cells, not depending on a relationship, dangling as leaves in the tree.
Inner cells, depending on a relationship between two cells, we will call them nodes.
Resulting tree never makes cycles.
Assumptions and Rationale
In his comments, #B.Mr.W. says, each relationship is formed by mathematical operators and there can be more than one nodes pointing to another node.
I assume he has relations like d = a - b * c. Evaluation of such expressions / relations has to follow operator precedence.
And, such expressions will be anyhow resolved to d = a - (b*c).
Evaluation of such expressions would result in sub-relationships which are again binary.
Note: These binary sub-relationships remain anonymous.
Requirements
Create new Leaf cells storing a number.
Create new Node cells by define relationships between other cells.
A Change to any cell should update all related cells, without affecting the relationship. That is, a cascading effect along the related branches.
Requirement 3. has to follow an ordering preference. The right-side node is preferred by default.
Solution Analysis
Both types of cells can be combined within or with other-type using a mathematical operator. (Implemented in class Cell)
I follow Python Data Model to solve this problem and interface style becomes:
# create new Leaf nodes with values:
a = Leaf(2)
b = Leaf(3)
c = Leaf(4)
# define relationships
d = a + b*c # (b*c) becomes anonymous
e = d - Leaf(3) # Leaf(3) is also anonymous
( ( 2 + ( 3 * 4 ) ) - 3 ) => { 'a':2, 'b':3, 'c':4, 'd':14, 'e':11 }
# get values of known nodes
a(), b(), c(), d(), e()
# update values
a(1) => { 'a':1, 'b':3, 'c':4, 'd':13, 'e':10 }
b(2) => { 'a':1, 'b':2, 'c':4, 'd':9, 'e':6 }
c(3) => { 'a':1, 'b':2, 'c':3, 'd':7, 'e':4 }
Code:
"""Super-type of Leaves and Nodes."""
class Cell:
"""Arithematic operation handlers between Leaves/Nodes"""
def __add__(self, other):
return Node(leftCell=self, rightCell=other, op='+')
def __sub__(self, other):
return Node(leftCell=self, rightCell=other, op='-')
def __mul__(self, other):
return Node(leftCell=self, rightCell=other, op='*')
def __truediv__(self, other):
return Node(leftCell=self, rightCell=other, op='/')
"""for clean presentation of float values"""
#staticmethod
def cleanFloat(val:int|float):
if isinstance(val, float):
rounded = round(val)
if abs(rounded-val)<0.011:
return rounded
else:
return round(val, 2)
return val
"""Leaves will contain values only"""
class Leaf(Cell):
def __init__(self, val:int|float):
self.val = val
"""Getter/Setter for Leaf val"""
#property
def val(self):
return self.cleanFloat(self.__val)
#val.setter
def val(self, val:int|float):
self.__val = self.cleanFloat(val)
"""Getter/Setter of Leaf object."""
def __call__(self, val:int|float=None):
if val == None:
return self.val
else:
self.val = val
def __str__(self):
return f"{self.val}"
"""Nodes contain left/right child, an arithematic operation and preferred side for update"""
class Node(Cell):
def __init__(self, leftCell:Cell, rightCell:Cell,
op:str, prefSide:str='right'):
self.leftCell = leftCell
self.rightCell = rightCell
self.op = op
self.prefSide = prefSide
"""
Preferred and the other cells for reverse path operation required during update.
These properties will help clean retrieval.
"""
#property
def preferredCell(self):
match self.prefSide:
case 'left' : return self.leftCell
case 'right': return self.rightCell
#property
def unPreferredCell(self):
match self.prefSide:
case 'left' : return self.rightCell
case 'right': return self.leftCell
"""Getter/Setter for Nodes"""
def __call__(self, val :int|float = None):
if val == None:
match self.op:
case '+':
nodeVal = self.leftCell() + self.rightCell()
case '-':
nodeVal = self.leftCell() - self.rightCell()
case '*':
nodeVal = self.leftCell() * self.rightCell()
case '/':
nodeVal = self.leftCell() / self.rightCell()
case _:
raise
return self.cleanFloat(nodeVal)
else:
match self.op:
case '+':
self.preferredCell( val - self.unPreferredCell() )
case '*':
self.preferredCell( val / self.unPreferredCell() )
case '-':
match self.prefSide:
case 'left' :
self.preferredCell( val + self.unPreferredCell() )
case 'right' :
self.preferredCell( self.unPreferredCell() - val )
case '/':
match self.prefSide:
case 'left ' :
self.preferredCell( val * self.unPreferredCell() )
case 'right' :
self.preferredCell( self.unPreferredCell() / val )
case _:
raise
def __str__(self):
return f"( {str(self.leftCell)} {self.op} {str(self.rightCell)} )"
if __name__ == '__main__':
def createTree():
# create new Leaf nodes having values
a = Leaf(2)
b = Leaf(3)
c = Leaf(4)
d = Leaf(5)
e = Leaf(6)
# define relationships
f = a + b
g = d / f - c # (d / f) becomes anonymous, higher precedence
h = Leaf(9) / e * g
# here, (Leaf(9)/e) creates the anonymous node, left to right associativity
return (a,b,c,d,e,f,g,h)
(a,b,c,d,e,f,g,h) = createTree()
def treeDict():
return f"{{ 'a':{a()}, 'b':{b()}, 'c':{c()}, 'd':{d()}, 'e':{e()}, 'f':{f()}, 'g':{g()}, 'h':{h()} }}"
print('\nget values of known cells:')
print(f"{h} => {treeDict()}\n")
print('each cell expanded (take care of anonymous cells):')
print(f"'a':{a}\n'b':{b}\n'c':{c}\n'd':{d}\n'e':{e}\n'f':{f}\n'g':{g}\n'h':{h}\n")
print('update values:')
a(1)
print( f"a(1) => {treeDict()}")
b(2)
print( f"b(2) => {treeDict()}")
c(3)
print( f"c(3) => {treeDict()}")
f(10)
print(f"f(10) => {treeDict()}")
g(10)
print(f"g(10) => {treeDict()}")
h(100)
print(f"h(100) => {treeDict()}")
print('\nchange ordering preference: g.prefSide = "left"')
(a,b,c,d,e,f,g,h) = createTree()
g.prefSide = 'left'
g(1)
print(f"g(1) => {treeDict()}")
print('\nchange ordering preference: g.prefSide = "left"')
(a,b,c,d,e,f,g,h) = createTree()
g.prefSide = 'left'
h(0)
print(f"h(0) => {treeDict()}")
print("\nAccessing Anonymous Cells:")
print(f"h.leftCell() : {h.leftCell()}")
print(f"h.leftCell.leftCell() : {h.leftCell.leftCell()}")
I wonder if this (using SymPy) gets at the desired behavior:
from sympy.abc import *
from sympy import *
ed = Tuple(
(Eq(d, a+b+c),c),
(Eq(g, e+f),f),
(Eq(h,d+g),g))
def indep(ed):
free = set()
dep = set()
for i,_ in ed:
assert i.lhs.is_Symbol
free |=i.rhs.free_symbols
dep |= {i.lhs}
return free - dep
def F(x, v, eq):
eq = list(eq)
free = indep(eq)
if x in free:
# leaf
return [i[0] for i in eq], free
else:
for i,y in eq:
if i.lhs == x:
return [i[0] for i in eq] + [Eq(x, v)], free - {y}
assert None, 'unknown parameter'
def update(x, v, ed):
eqs, ex = F(x, v, ed)
sol = solve(eqs, exclude=ex, dict=True)
assert len(sol) == 1
return Dict(sol), list(ordered(ex))
ed represents the list of equations and the defined "dependent/rightmost" variable for each equation, however you want to define it.
So if you want to update g = 10 you would have the following values:
>>> update(g, 10, ed)
({d: a + b + c, f: 10 - e, g: 10, h: a + b + c + 10}, [a, b, c, e])
The variables on the right are the ones that you would have to specify in order to get known values for all others
>>> _[0].subs(dict(zip(_[1],(1,1,1,1))))
{d: 3, f: 9, g: 10, h: 13}
this is my code to find the height of a tree of up to 10^5 nodes. May I know why I get the following error?
Warning, long feedback: only the beginning and the end of the feedback message is shown, and the middle was replaced by " ... ". Failed case #18/24: time limit exceeded
Input:
100000
Your output:
stderr:
(Time used: 6.01/3.00, memory used: 24014848/2147483648.)
Is there a way to speed up this algo?
This is the exact problem description:
Problem Description
Task. You are given a description of a rooted tree. Your task is to compute and output its height. Recall
that the height of a (rooted) tree is the maximum depth of a node, or the maximum distance from a
leaf to the root. You are given an arbitrary tree, not necessarily a binary tree.
Input Format. The first line contains the number of nodes π. The second line contains π integer numbers
from β1 to π β 1 β parents of nodes. If the π-th one of them (0 β€ π β€ π β 1) is β1, node π is the root,
otherwise itβs 0-based index of the parent of π-th node. It is guaranteed that there is exactly one root.
It is guaranteed that the input represents a tree.
Constraints. 1 β€ π β€ 105
Output Format. Output the height of the tree.
# python3
import sys, threading
from collections import deque, defaultdict
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27) # new thread will get stack of such size
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
height = 0
nodes = [[] for _ in range(self.n)]
for child_index in range(self.n):
if self.parent[child_index] == -1:
# child_index = child value
root = child_index
nodes[0].append(root)
# do not add to index
else:
parent_index = None
counter = -1
updating_child_index = child_index
while parent_index != -1:
parent_index = self.parent[updating_child_index]
updating_child_index = parent_index
counter += 1
nodes[counter].append(child_index)
# nodes[self.parent[child_index]].append(child_index)
nodes2 = list(filter(lambda x: x, nodes))
height = len(nodes2)
return(height)
def main():
tree = TreeHeight()
tree.read()
print(tree.compute_height())
threading.Thread(target=main).start()
First, why are you using threading? Threading isn't good. It is a source of potentially hard to find race conditions and confusing complexity. Plus in Python, thanks to the GIL, you often don't get any performance win.
That said, your algorithm essentially looks like this:
for each node:
travel all the way to the root
record its depth
If the tree is entirely unbalanced and has 100,000 nodes, then for each of 100,000 nodes you have to visit an average of 50,000 other nodes for roughly 5,000,000,000 operations. This takes a while.
What you need to do is stop constantly traversing the tree back to the root to find the depths. Something like this should work.
import sys
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
height = [None for _ in self.parent]
todo = list(range(self.n))
while 0 < len(todo):
node = todo.pop()
if self.parent[node] == -1:
height[node] = 1
elif height[node] is None:
if height[self.parent[node]] is None:
# We will try again after doing our parent
todo.append(node)
todo.append(self.parent[node])
else:
height[node] = height[self.parent[node]] + 1
return max(height)
if __name__ == "__main__":
tree = TreeHeight()
tree.read()
print(tree.compute_height())
(Note, I switched to a standard indent, and then made that indent 4. See this classic study for evidence that an indent in the 2-4 range is better for comprehension than an indent of 8. And, of course, the pep8 standard for Python specifies 4 spaces.)
Here is the same code showing how to handle accidental loops, and hardcode a specific test case.
import sys
class TreeHeight:
def read(self):
self.n = int(sys.stdin.readline())
self.parent = list(map(int, sys.stdin.readline().split()))
def compute_height(self):
height = [None for _ in self.parent]
todo = list(range(self.n))
in_redo = set()
while 0 < len(todo):
node = todo.pop()
if self.parent[node] == -1:
height[node] = 1
elif height[node] is None:
if height[self.parent[node]] is None:
if node in in_redo:
# This must be a cycle, lie about its height.
height[node] = -100000
else:
in_redo.add(node)
# We will try again after doing our parent
todo.append(node)
todo.append(self.parent[node])
else:
height[node] = height[self.parent[node]] + 1
return max(height)
if __name__ == "__main__":
tree = TreeHeight()
# tree.read()
tree.n = 5
tree.parent = [-1, 0, 4, 0, 3]
print(tree.compute_height())
I'm currently creating a genetic algorithm and am trying to only get certain values from the ASCII table so the runtime of the algorithm can be a bit faster. In the code below I get the values between 9-127 but I only need the values 9-10, and 32-127 from the ASCII table and I'm not sure on how to exactly only get those specific values. Code below is done in python.
import numpy as np
TARGET_PHRASE = """The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""" # target DNA
POP_SIZE = 4000 # population size
CROSS_RATE = 0.8 # mating probability (DNA crossover)
MUTATION_RATE = 0.00001 # mutation probability
N_GENERATIONS = 100000
DNA_SIZE = len(TARGET_PHRASE)
TARGET_ASCII = np.fromstring(TARGET_PHRASE, dtype=np.uint8) # convert string to number
ASCII_BOUND = [9, 127]
class GA(object):
def __init__(self, DNA_size, DNA_bound, cross_rate, mutation_rate, pop_size):
self.DNA_size = DNA_size
DNA_bound[1] += 1
self.DNA_bound = DNA_bound
self.cross_rate = cross_rate
self.mutate_rate = mutation_rate
self.pop_size = pop_size
self.pop = np.random.randint(*DNA_bound, size=(pop_size, DNA_size)).astype(np.int8) # int8 for convert to ASCII
def translateDNA(self, DNA): # convert to readable string
return DNA.tostring().decode('ascii')
def get_fitness(self): # count how many character matches
match_count = (self.pop == TARGET_ASCII).sum(axis=1)
return match_count
def select(self):
fitness = self.get_fitness() # add a small amount to avoid all zero fitness
idx = np.random.choice(np.arange(self.pop_size), size=self.pop_size, replace=True, p=fitness/fitness.sum())
return self.pop[idx]
def crossover(self, parent, pop):
if np.random.rand() < self.cross_rate:
i_ = np.random.randint(0, self.pop_size, size=1) # select another individual from pop
cross_points = np.random.randint(0, 2, self.DNA_size).astype(np.bool) # choose crossover points
parent[cross_points] = pop[i_, cross_points] # mating and produce one child
return parent
def mutate(self, child):
for point in range(self.DNA_size):
if np.random.rand() < self.mutate_rate:
child[point] = np.random.randint(*self.DNA_bound) # choose a random ASCII index
return child
def evolve(self):
pop = self.select()
pop_copy = pop.copy()
for parent in pop: # for every parent
child = self.crossover(parent, pop_copy)
child = self.mutate(child)
parent[:] = child
self.pop = pop
if __name__ == '__main__':
ga = GA(DNA_size=DNA_SIZE, DNA_bound=ASCII_BOUND, cross_rate=CROSS_RATE,
mutation_rate=MUTATION_RATE, pop_size=POP_SIZE)
for generation in range(N_GENERATIONS):
fitness = ga.get_fitness()
best_DNA = ga.pop[np.argmax(fitness)]
best_phrase = ga.translateDNA(best_DNA)
print('Gen', generation, ': ', best_phrase)
if best_phrase == TARGET_PHRASE:
break
ga.evolve()
You need a customed method to generate random samples in range 9-10, and 32-127, like
def my_rand(pop_size, DNA_size):
bold1=[9,10]
bold2=list(range(32,127))
bold=bold1+bold2
pop = np.random.choice(bold,(pop_size,DNA_size)).astype(np.int8)
return pop
then call this method to replace the line 29, like
delete -- self.pop = np.random.randint(*DNA_bound, size=(pop_size, DNA_size)).astype(np.int8) # int8 for convert to ASCII
call ---self.pop = my_rand(pop_size, DNA_size)
I've been working on creating a bfs search algorithm on a 2D grid in Python (object-oriented). I have a Grid class that takes care of setting up the 2D grid and has some member functions to mark walls, start/end points, etc.
class Grid:
def __init__(self, nodes=[]):
self.nodes = nodes
# Initializes a grid of rl x rc and fills all nodes with and empty value of 1
def init_grid(self, rl, cl):
arr = []
arr2 = []
for i in range(rl):
for j in range(cl):
arr2.append(Node("1", i, j))
arr.append(arr2)
arr2 = []
self.nodes = arr
# Sets the start and end points at the specified rows and cols
def set_start_and_end(self, startr, startc, endr, endc):
startNode = Node("S", startr, startc)
endNode = Node("E", endr, endc)
self.nodes[startr][startc] = startNode
self.nodes[endr][endc] = endNode
def mark_visited(self, row, col):
self.nodes[row][col].val = "V"
def mark_wall(self, row, col):
self.nodes[row][col].val = "X"
I also have a file that will contain all my search algorithms (just BFS for now). In one of my helper functions for finding neighbours of a node, it takes in a node object as well as a grid object.
def get_neighbors(self, node, grid):
neighbours = []
R = len(grid)
C = len(grid[0])
dr = [-1, +1, 0, 0]
dc = [0, 0, +1, -1]
for i in range(4):
rr = node.r + dr[i]
cc = node.c + dc[i]
if rr < 0 or cc < 0: continue
if rr >= R or cc >= C: continue
neighbour = grid[rr][cc]
neighbours.append(neighbour)
return neighbours
I'm new to Python, and come from typecast languages, but is there a way to typecast my node and grid objects in some way? As of right now I don't need to instantiate either the grid or node objects, but I'm not sure how to access them otherwise. Unless I'm just going about this the wrong way.
Your grid class functions depend on self so you must instantiate to access as it is now.
If you want static behavior just put class attributes outside of your constructor:
class Grid():
nodes = []
def __init__(self):
#staticmethod
def init_grid(param1, param2):
#Do stuff
Grid.nodes = arr
Then in your other file from filename import Grid and you should be able to access the static method with Grid.init_grid() and access nodes with Grid.nodes If you can't, you can get an "static" object with g_type = eval("Grid") then you can access your "static" members with g_type.init_grid()
Hope that helps.
I am learning about class by building a class that represents polynomials. I know that theres a nice way of doing polynomials in numpy btw.
I have an add, multiply and call dunner methods defined. Polynomials are sums of terms of the form a*x**b so the class is called 'sot'.
Any instance of sot contains self.term which is a list of pears of numbers [a,b]. each per represent a*x**b.
the add method returns a new instance of sot with the concatenation of self.terms with other.terms as the argument
sot take a list of pears as its argument in its initialization
Thee problem is that when I add two instances of sot a+b a is changed, and so that if I repeat the addition a+b I get a different result.
The same does not happen with my multiplication method.
Can anyone see what's going on here?
#The class 'sot' represents a Sum Of Terms of the form a*x^b, aka a polinomeal
#On creating a new instanse, sot must be fead a list containg its terms.
#Each terms is represented by a list of two vareavles. [a,b] represesnting a*x^b
#Eg: a = sot(( [1,2], [2,1], [3,0] )) represents x^2 + 2x + 3
#Caling an instanse will return the evaluation of the polinomeal
# If the terms of sot are given as a tuple (eg sot(( (1,2) )) ) the object will not function as intended.
# Creating a sot of no terms, and empty polinomeal, must be done with squer brackets. eg sot([[0,3]])
# This vesion of the file prints out what its doing in the simplifying step, this is for debuging perposes
class sot:
def __init__(self,tupe):
#The first step un-packs the argument into a list of terms
self.term=[]
for t in tupe:
print("add " + str(t) + " to " + str(self.term))
self.term = self.term+[t]
#Simpler is a method that simplifys the polinomeal
self.simpler()
def simpler(self):
#The first step is to sort the polinnomeal by the assending powers eg c + x + x^2 ...
print("simplifying " + str(self))
self.term = sorted(self.term,key = lambda element: element[1] )
print("sorted "+str(self))
# now this while loop will inspect the list of terms to see if anny
# (a) have a zero coefichent
# in which case there removed from the list
# (b) have the same power
# in which case the terms second of ther terms will be poped form the list
# sotored in 'carry' and its constant 'carry[0]' will be added to the first term
# If the loop has not run then ther must be onlly one element and it must not have been
# checked for beeing zero
i=0
while i < len(self.term)-1:
print("checking " + str(i) + " out of " + str( len(self.term)-2))
if self.term[i][0] ==0:
self.term.remove(i)
print("removeing zero")
if self.term[i][1] ==self.term[i+1][1]:
print("adding similers")
carry = self.term.pop(i+1)
print("carry term "+str(carry))
self.term[i][0] = self.term[i][0] +carry[0]
print("new sum "+ str(self))
i=i+1
if i==0 & self.term[0][0] == 0:
self.term = []
print("the list has one term and that term is zero")
print("simplifyed compleat " + str(self))
def __call__(self,x):
# the result of a call the sum of each term, remember that each term [a,b] represents a*x**b
result = 0
for t in self:
result = result + t[0]*x**t[1]
return result
def __repr__(self):
string = []
for t in self:
string = string+ [str(t[0]) + "x**" + str(t[1])]
return " + ".join(string)
def __len__(self):
#The length of a sot is just the number of terms
return len(self.term)
def __iter__(self):
# The iteration of a sot steps throgh the list of terms starting from the zerothe
# and returning each tem as it dose so
self.i=0
return self
def __next__(self):
if self.i < len(self):
self.i=self.i+1
return self.term[self.i-1]
else:
raise StopIteration
def __add__(self,other):
# adding tow sots together is the same as haveing a new sot, so for a + b there list of terms
# are concatanated and fed into a new instanse of sot
argument = self.term+other.term
return sot(argument)
def __mul__(self,other):
# multiplication of sots is done with the distributive law:
# sum_i(x_i)*sum_j(x_j) = sum_i(x_i *sum_j(x_j)) = sum_i(sum_j(x_i*x_j))
# this is realised with two imbeded for loops
# when two terms are multiplyed, the constant parts are multiplyed and the powers are added:
#(ax**b)*(cx**d) = (a*c)x**(b+d)
# The relustant list of terms is fed into a new instanse of sot
argument = []
for s in self:
for o in other:
argument = argument + [[ s[0]*o[0] , s[1]+o[1] ]]
return sot(argument)