Last active
October 4, 2020 13:26
-
-
Save chaidhat/0f080e0bb1fefcf73c39ccd7f02bf0fd to your computer and use it in GitHub Desktop.
Approximates simultaneous equations by brute force. Works with multiple solutions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Chaidhat Chaimongkol | |
# 02-10-20 for 5 hours | |
# I have developed a brute-forcer for simultaneous equations with two variables and can work with equations with more than one solution. Below is an example of how to use it. | |
import parser | |
import math | |
import copy | |
from math import sin | |
# const 10^PRECISION_THRESHOLD is the allowable difference in floating point values to be considered equal | |
PRECISION_THRESHOLD = 10 | |
# const is the starting point for lowest error. Should be much higher than expected error for the program. | |
HIGHEST_ERROR = 10**10 | |
class Variable: | |
def __init__(self, name, test_min, test_max, test_step): | |
self.name = name | |
self.test_min = test_min | |
self.test_max = test_max | |
self.test_step = test_step | |
self.val = self.test_min | |
self.lowest_error = HIGHEST_ERROR | |
self.child = None | |
def Simulate(self, f_codes, variables, lowest_error): | |
# append our variables to parent's variables | |
outputs = [] | |
while self.val < self.test_max: | |
# update our variable with current value | |
variables[-1] = round(self.val, PRECISION_THRESHOLD) | |
# is this the bottomost child? | |
if self.child == None: | |
# YES: evaluate the final results | |
error = 0 | |
# evaluate all formulae | |
for f_code in f_codes: | |
output = eval(f_code) | |
error += abs(output) | |
if error < lowest_error[0] + 10**-PRECISION_THRESHOLD: | |
lowest_error[0] = error | |
outputs.append((error, copy.copy(variables))) | |
else: | |
# NO: recurse/branch deeper | |
# append a variable for child use (lists are mutable) | |
variables.append(0) | |
child = copy.copy(self.child) | |
# recursive simulate | |
outputs.extend(child.Simulate(f_codes, variables, lowest_error)) | |
# pop the child's variable out | |
variables.pop() | |
# iterate | |
self.val += self.test_step | |
return outputs | |
def findSimul(formulae, variables): | |
# interface user formulae variable names to actual python identifiers | |
for i, formula in enumerate(formulae): | |
for j, variable in enumerate(variables): | |
formula = formula.replace(variable.name, "[" + str(j) + "]") | |
# final replace, to not have overlapping replaces | |
formulae[i] = formula.replace("[", "variables[") | |
# compile the formulae | |
f_codes = [] | |
for formula in formulae: | |
f_codes.append(parser.expr(formula).compile()) | |
# assign child for recursive evaluation | |
for index, variable in enumerate(variables[:-1]): | |
variable.child = variables[index + 1] | |
# lowest_error is a list to force mutability | |
lowest_error = [HIGHEST_ERROR] | |
# recursively evaluate | |
outputs = variables[0].Simulate(f_codes, [0], lowest_error) | |
# prune errors | |
solutions = [] | |
for output in outputs: | |
error = output[0] | |
# if error == closest error | |
if error <= lowest_error[0] + 10**-PRECISION_THRESHOLD: | |
solution = output[1] | |
for i, solution_n in enumerate(solution): | |
# round floating point errors | |
solution[i] = variables[i].name + " = " + str(round(solution_n, PRECISION_THRESHOLD)) | |
# append only x and y | |
solutions.append(solution) | |
if lowest_error[0] > 1: | |
print("ERROR HIGH", lowest_error[0]) | |
return solutions | |
print("Input amounnt of variables to simulate") | |
answer = int(input("> ")) | |
variables = [] | |
for i in range(answer): | |
print("\tVar", i+1, ": Input name of variable (e.g. x)") | |
variable_name = input("\t> ") | |
print("\tVar", i+1, ": Input minimum test value") | |
variable_min = float(input("\t> ")) | |
print("\tVar", i+1, ": Input maximum test value") | |
variable_max = float(input("\t> ")) | |
print("\tVar", i+1, ": Input step size") | |
variable_step = float(input("\t> ")) | |
variables.append(Variable(variable_name, variable_min, variable_max, variable_step)) | |
print("\n") | |
print("Input amount of formulae to compare") | |
answer = int(input("> ")) | |
formulae = [] | |
for i in range(answer): | |
print("\tFormula", i+1, ": Input formula") | |
formulae.append(input("\t> 0 = ")) | |
print(findSimul(formulae, variables)) | |
""" | |
# you can also use code to figure out variables | |
# we define our variables | |
# minimum test, maximum test | |
# we can also specify a step size | |
# example one: equation with more than one solution | |
x = Variable("x", -20, 20, 0.5) | |
y = Variable("y", -20, 20, 0.5) | |
print(findSimul(["x**2 - x - 6 - y", "6 - 2*x - y"], [x, y])) | |
# example two: find equations with one variable | |
x = Variable("x", -20, 20, 0.0001) | |
print(findSimul(["x**2 - 2"], [x])) | |
# example three: 4 equation with 4 variables | |
w = Variable("w", -10, 10, 1) | |
x = Variable("x", -10, 10, 1) | |
y = Variable("y", -10, 10, 1) | |
z = Variable("z", -10, 10, 1) | |
print(findSimul(["(x + 2*y - 3*z + 4*w) - 12", "(2*x + 2*y - 2*z + 3*w) - 10", "y + z - 1", "x - y + z - 2*w + 4"], [w, x, y, z])) | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I have developed a powerful brute-forcer for simultaneous equations with multiple variables and can work with equations with more than one solution. You can specify multiple formulae which uses multiple variables and the program will return the solutions (can be more than one) with the lowest error.
Below is an example of how to use it.
Task
An exponential graph increases from a horizontal asymptote of y = 10. Passes through (0,20) and (10,40).
The graph would use this format of equation:
y = a^(x+b) + 10
To find out constants a and b we can make two simultaneous equations.
a^b + 10 = 20
a^(b+10) + 10 =40
To input into the program, we must rearrange so that the equations are both equal to 0.
a^b - 10 = 0
a^(b+10) - 30 = 0
In the code below, we change 'a' to 'x' and 'b' to 'y'
To save computational time, we can estimate x to be between 0 and 2 and y to be between 0 and 25. We ask it to find to 0.01 precision (step size).
Input
Result
My approximate:
x ≈ 1.12, y ≈ 20.01 in about 10 seconds
Theoretical solution:
x = 1.1161..., y = 20.9590...
Graph