Quadratic Formula in Python - python

Here is my quadratic formula code. You enter in the full equation, and it outputs the roots. The problem is, my code fully depends on the index spot of where the number is in the list, and if I were to miss a space somewhere while writing my code, it would not work. Anyone know how to fix this?
import re
import cmath
quadratic = str(input('Enter a quadratic equation (in the form of ax^2 + bx + c): '))
split_qua = re.split(' | x |', quadratic)
a1 = (split_qua[1])
b1 = (split_qua[8])
c1 = (split_qua[13])
a = int(a1)
b = int(b1)
c = int(c1)
def roots(a,b,c):
dis = b*b-4*a*c
if dis >=0:
root1 =(-b + cmath.sqrt(dis) / 2*a)
root2 =(-b - cmath.sqrt(dis) / 2*a)
return root1, root2
elif dis < 0:
return 'This does not work. There is no roots'
print(roots(a,b,c))

You could try removing all the spaces and then parsing your input string:
a,b,c = map(int, [s.split("x")[0] for s in quadratic.replace(" ","").split("+")])

Here's a brute force parser that ignores spaces and handles signs and floats. It will also handle missing values (3x^2-9).
def parse_equation(s):
nums = [0,0,0]
accum = ''
expo = False
sign = 1
for c in s:
if expo and c == '2':
expo = False
nums[0] = nums[1]
nums[1] = 0
accum = ''
elif c in '1234567890.':
accum += c
elif c == 'x':
nums[1] = float(accum) * sign
accum = ''
elif c == '+':
sign = 1
elif c == '-':
sign = -1
elif c == '^':
expo = True
nums[2] = float(accum) * sign
return nums
for test in (
'3x^2+4x-3',
'3x^2 + 4x - 3',
'-19.5 x^2 + 7.5 x - 3'
):
print(test, parse_equation(test))
Output:
3x^2+4x-3 [3.0, 4.0, -3.0]
3x^2 + 4x - 3 [3.0, 4.0, -3.0]
-19.5 x^2 + 7.5 x - 3 [-19.5, 7.5, -3.0]

You can extract the numbers and check the equation structure in one regex expression
import re
import cmath
quadratic = str(input('Enter a quadratic equation (in the form of ax^2 + bx + c): '))
parts = re.findall(r'(\d+)x\^2\s*\+\s*(\d+)x\s*\+\s*(\d+)', quadratic)
try:
a, b, c = [int(x) for x in parts[0]]
except:
raise RuntimeError('Incorrect equation')
def roots(a, b, c):
dis = b * b - 4 * a * c
if dis >= 0:
root1 = (-b + cmath.sqrt(dis) / 2 * a)
root2 = (-b - cmath.sqrt(dis) / 2 * a)
return root1, root2
elif dis < 0:
return 'This does not work. There is no roots'
print(roots(a, b, c))
Output:
Enter a quadratic equation (in the form of ax^2 + bx + c): 4x^2 + 8x + 1
((5.8564064605510175+0j), (-21.856406460551018+0j))

You're running into a problem with your regular expression on this line:
split_qua = re.split(' | x |', quadratic)
I'm not entirely sure how you came up with that regex, but it's effectively splitting the string in any of the following situations:
Between spaces
Between any occurrence of the string " x " (that's an "x" with a space on either side)
Between all characters
Situation #3 is always going to win.
Without getting too much into the syntax of regular expressions--a topic that you may want to research on your own--all of the characters in your regex are being taken literally except the vertical bar (|). That is effectively acting as an "or" operator: split between " " or " x " or "". That last one is giving you trouble.
The ideal solution here would be to parse everything properly rather than using regular expressions; however, since this is likely just a quick script for personal use, you can certainly craft a better regex. It won't be ideal, but it'll likely work well enough for your purposes. For example:
split_qua = re.fullmatch(r'([+-]?\d+)?x\^2([+-]\d+)?x([+-]\d+)?', quadratic.replace(' ', ''))
a = int(split_qua[1] or 1)
b = int(split_qua[2] or 1)
c = int(split_qua[3] or 0)
Let's break down the regex:
This isn't actually part of the regex, but notice that we're calling replace() to remove all the whitespace. This just allows us to use a simpler regex, since we don't need to worry about parsing spaces.
Each number in which we're interested is parsed via ([+-]?\d+) or ([+-]\d+). The first one is used when we don't know whether there will be a sign; that only happens for a.
The parentheses are used for grouping; the first set of parentheses will be stored in split_qua[1] and so on.
[+-] means "either + or -"
? means the previous token is optional--in this case, it means the sign is optional
\d means "any digit"
+ means "repeated one or more times," that way we can have numbers that are more than one digit long
x matches the literal character "x"
\^ matches the literal character "^". Carets have a special meaning in regular expressions, so we have to escape them.
The ? following clothing parenthesis of each group indicates that it's optional. If a or b are omitted, we'll assume their value is 1; if c is omitted, we'll assume its value is 0.
This technique is far from perfect: in particular, it can't parse expressions in which one of the first two terms is omitted. For example, 3x^2 + 1 will fail to parse. It's up to you whether this is worth handling; if it is, you should investigate using proper parsing logic rather than regular expressions. If this doesn't matter to you, the regular expression here may be sufficient.

You can do like this.
Remove the spaces from the input using .replace()
Split the string at + to get individual strings.
a,b,c = quadratic.replace(" ","").split('+')
Strip off the powers of x from the individual strings using
rstrip() and convert them to Integers.
a = int(a.rstrip('x^2'))
b = int(b.rstrip('x'))
c = int(c)
Here is the full code:
import cmath
quadratic = str(input('Enter a quadratic equation (in the form of ax^2 + bx + c): '))
a,b,c = quadratic.replace(" ","").split('+')
a = int(a.rstrip('x^2'))
b = int(b.rstrip('x'))
c = int(c)
def roots(a,b,c):
dis = b*b-4*a*c
if dis >=0:
root1 =(-b + cmath.sqrt(dis) / 2*a)
root2 =(-b - cmath.sqrt(dis) / 2*a)
return root1, root2
elif dis < 0:
return 'This does not work. There is no roots'
print(roots(a,b,c))

Related

python How to convert the input value into a mathematical function

How to convert an input value into a function!
x = int(input('Enter x value: '))
n = str(input('Enter n value: ')) #n= 2 * x ^ 2 - 2 * x + 2
def f(x,n):
return 2 * x ^ 2 - 2 * x + 2
Actually for what i understand, you don't need to input n.
x = int(input('Enter x value: '))
def f(x):
return 2*x**2 - 2*x+2
n = f(x)
Edit, after rereading others answer yes it probably wanted eval()
Just You can't write "2 * x ^ 2 - 2 * x + 2", the correct way is x**2 instead of x^2
You mean (?):
def f(x, n):
return n*x**2 - 2*x + 2
Or do you mean actually changing the operators?
The question as currently posed is mathematically impossible. You define x & n and are returning a function that you may or may not want to equate to n but its all defined entries.
Still guessing a little at the actual question, but if
y = input("Enter equation to evaluate")
and you expect y to be a quadratic, i.e.:
y = "a*x**b - c*x + d"
then you can get all them from:
import re
y1 = re.split('[* ]',y)
a = y1[0]
b = y1[3] #as there is a null ent between the ** we skip y1[2] and y[1] is 'x'
c = y1[5]
d = y1[8]
If you wanted the operators, then it gets a little harder to follow. So we'll cross that bridge if you do need them.
Or, as the others suggest, just use eval()!!
You could try to use eval.
x=int(input('...'))
n=input('...') # note that input returns a string
def f(x):
global n
return(eval(n))
I think,you are asking .
How to convert an input value into a mathmetical expression ?
If it is so !
use eval()
in python

Is there a way to reuse some of my code, like a function?

So i'm some what new to python and as an exercise, I tried to make a derivative calculator that is completely inclusive(not importing anything). But i don't know how to expand the code i wrote for the first term for an x amount of terms, So is there a way to apply the same code i wrote for the first term and apply it to all the other potential terms? maybe in the form of a function?
Here is the code i have written so far(sorry if it is messy, i am fairly new):
def pwr_rule(a, n):
a = float(a)
n = float(n)
a = a*n
n = n-1
return a, n;
input_string = input("Enter coefficient, base and exp. seperated by comma ")
term1 = input_string.split(", ")
#removes spaces, commas and quote. marks
a, x, n = [term1[i] for i in (0, 1, 2)]
#a is coeff
#x is base
#n is exp.
a, n = pwr_rule(a, n)
while True:
if (n == 1):
print(str(int(a)) + x)
elif (n > 1):
print(str(int(a)) + x + '^' +str(int(n)))
elif (n == 0):
print(str(int(a)))
r = 0
r += 1
if (r > 0):
break
edit: im just now realizing that the variable, x, is redundant, just disregard it

Random picking of operators (add,sub) with conditions

import operator
import random
# these modules will help with the random picking of operators
ops = {"+": operator.add, "-": operator.sub}
a = ["+", "-"] #this is to do the random.choice() function
b = float(ops[random.choice(a)](0, 1)) #these can only take 2 parameters, don't ask me why
c = float(ops[random.choice(a)](2, 3))
d = float(ops[random.choice(a)](4, 5))
e = float(ops[random.choice(a)](6, 7))
f = float(ops[random.choice(a)](8, 9))
print(b+c+d+e+f) #I have no idea how to do random operators here so i just added
while b+c+d+e+f != 5: #to over and over until i get 5
if b+c+d+e+f == 5: #if that finally happens...
print(b+c+d+e+f) #print 5
So the idea is to use random operators between numbers 0-9 to get 5.
I literally don't know what code to use. The operator.add() function only take 2 parameters.
if anybody can give me an idea how to enter more parameters, That would really help.
To put it simply, the numbers 0,1,2,3,4,5,6,7,8,9 need to have + or minus go between each number, to get the number 5.
This is probably not the most efficient or elegant method, but it will do the trick. Always put your while loop around what needs to be done. In your example, only b+c+d+e+f is only calculated over and over again while the operators and values for b, c, d, e and f do not change.
import random
result = 0
result_str = ""
while result != 5:
result = 0
result_str = ""
for i in range(0,10):
# here is random decision between operators
if bool(random.getrandbits(1)):
result += i
result_str = result_str + "+" + str(i)
else:
result -= i
result_str = result_str + "-" + str(i)
print(result_str)

Testing equivalence of mathematical expressions in Python

I have got two strings in Python,
A m * B s / (A m + C m)
and
C m * B s / (C m + A m)
that are both equivalent functions of the unordered set (A, C) and the unordered set (B). m and s indicate units that can be swapped among the same but not with another unit.
So far, I'm doing permutations of A, B, and C and testing them using eval and SymPy's == operator. This has multiple drawbacks:
for more complicated expressions, I have to generate a large number of permutations (in my case 8 nested for loops)
I need to define A, B, C as symbols, which is not optimal when I don't know which parameters I will have (so I have to generate all of them -> terribly inefficient and messing up my variable namespace)
Is there a pythonian way to test for this kind of equivalence? It should work an arbitrary expressions.
Here is a simplified approach based on my previous answer.
The idea is that if two expressions are equivalent under permutations, the permutation carrying one to the other must map the ith symbol in the first string (ordered by index of first occurrence) to the ith symbol in the second string (again ordered by index of first occurrence). This principle can be used to construct a permutation, apply it to the first string and then check for equality with the second string - if they are equal they are equivalent, otherwise they are not.
Here is one possible implementation:
import re
# Unique-ify list, preserving order
def uniquify(l):
return reduce(lambda s, e: s + ([] if e in s else [e]), l, [])
# Replace all keys in replacements with corresponding values in str
def replace_all(str, replacements):
for old, new in replacements.iteritems():
str = str.replace(old, new)
return str
class Expression:
units = ["m", "s"]
def __init__(self, exp):
self.exp = exp
# Returns a list of symbols in the expression that are preceded
# by the given unit, ordered by first appearance. Assumes the
# symbol and unit are separated by a space. For example:
# Expression("A m * B s / (A m + C m)").symbols_for_unit("m")
# returns ['A', 'C']
def symbols_for_unit(self, unit):
sym_re = re.compile("(.) %s" % unit)
symbols = sym_re.findall(self.exp)
return uniquify(symbols)
# Returns a string with all symbols that have units other than
# unit "muted", that is replaced with the empty string. Example:
# Expression("A m * B s / (A m + C m)").mute_symbols_for_other_units("m")
# returns "A m * s / (A m + C m)"
def mute_symbols_for_other_units(self, unit):
other_units = "".join(set(self.units) - set(unit))
return re.sub("(.) ([%s])" % "".join(other_units), " \g<2>", self.exp)
# Returns a string with all symbols that have the given unit
# replaced with tokens of the form $0, $1, ..., by order of their
# first appearance in the string, and all other symbols muted.
# For example:
# Expression("A m * B s / (A m + C m)").canonical_form("m")
# returns "$0 m * s / ($0 m + $1 m)"
def canonical_form(self, unit):
symbols = self.symbols_for_unit(unit)
muted_self = self.mute_symbols_for_other_units(unit)
for i, sym in enumerate(symbols):
muted_self = muted_self.replace("%s %s" % (sym, unit), "$%s %s" % (i, unit))
return muted_self
# Define a permutation, represented as a dictionary, according to
# the following rule: replace $i with the ith distinct symbol
# occurring in the expression with the given unit. For example:
# Expression("C m * B s / (C m + A m)").permutation("m")
# returns {'$0':'C', '$1':'A'}
def permutation(self, unit):
enum = enumerate(self.symbols_for_unit(unit))
return dict(("$%s" % i, sym) for i, sym in enum)
# Return a string produced from the expression by first converting it
# into canonical form, and then performing the replacements defined
# by the given permutation. For example:
# Expression("A m * B s / (A m + C m)").permute("m", {"$0":"C", "$1":"A"})
# returns "C m * s / (C m + A m)"
def permute(self, unit, permutation):
new_exp = self.canonical_form(unit)
return replace_all(new_exp, permutation)
# Test for equality under permutation and muting of all other symbols
# than the unit provided.
def eq_under_permutation(self, unit, other_exp):
muted_self = self.mute_symbols_for_other_units(unit)
other_permuted_str = other_exp.permute(unit, self.permutation(unit))
return muted_self == other_permuted_str
# Test for equality under permutation. This is done for each of
# the possible units using eq_under_permutation
def __eq__(self, other):
return all([self.eq_under_permutation(unit, other) for unit in self.units])
e1 = Expression("A m * B s / (A m + C m)")
e2 = Expression("C m * B s / (C m + A m)")
e3 = Expression("A s * B s / (A m + C m)")
f1 = Expression("A s * (B s + D s) / (A m + C m)")
f2 = Expression("A s * (D s + B s) / (C m + A m)")
f3 = Expression("D s")
print "e1 == e2: ", e1 == e2 # True
print "e1 == e3: ", e1 == e3 # False
print "e2 == e3: ", e2 == e3 # False
print "f1 == f2: ", f1 == f2 # True
print "f1 == f3: ", f1 == f3 # False
As you pointed out, this checks for string equivalence under permutations without any regard to mathematical equivalence, but it is half the battle. If you had a canonical form for mathematical expressions, you could use this approach on two expressions in canonical form. Perhaps one of sympy's Simplify could do the trick.
Instead of iterating over all possible permutations, assume one exists and attempt to construct it. I believe that done in the right way, failure of the algorithm would imply inexistence of the permutation.
Here is the outline of the idea applied to the expressions above:
let:
str1 = "A m * B s / (A m + C m)"
str2 = "C m * B s / (C m + A m)"
We're looking for a permutation of the set (A, C) that would render the expressions identical. Relabel A and C as X1 and X2 according to the order of their first appearance in str2, so:
X1 = C
X2 = A
because C appears before A in str2. Next, create the array Y such that y[i] is the ith symbol A or C in order of first appearance in str1. So:
Y[1] = A
Y[2] = C
Because A appears before C in str1.
Now construct str3 from str2 by replacing A and C with X1 and X2:
str3 = "X1 m * B s / (X1 m + X2 m)"
And then start substituting Xi for Y[i]. First, X1 becomes Y[1]=A:
str3_1 = "A m * Bs / (A m + X2 m)"
At this stage, compare str3_1 and str1 up to the first occurrence of any of the Xi's, in this case X2, so because these two strings are equal:
str3_1[:18] = "A m * B s / (A m + "
str1[:18] = "A m * B s / (A m + "
You have a chance of constructing the permutation. If they were unequal, you'd have proven no suitable permutation exists (because any permutation would have had to make at least that substitution) and could deduce inequivalence. But they are equal, so you proceed to the next step, substituting X2 for Y[2]=C:
str3_2 = "A m * B s / (A m + C m)"
And this is equal to str1, so you have your permutation (A->C, C->A) and have shown the equivalence of the expressions.
This is only a demonstration of the algorithm to a particular case, but it should generalize. Not sure what the lowest order you could get it down to is, but it should be quicker than the n! of generating all permutations on n variables.
If I understand the significance of the units correctly, they limit which variables may be swapped for which others by the permutations. So that A can be substituted with C in the above expressions because both have 'm' units, but not with B which has 's' units. You can handle this in the following way:
construct expressions str1_m and str2_m from str1 and str2 by removing all symbols that don't have m units, and then carry out the above algorithm for str1_m and str2_m. If construction fails, no permutation exists. If construction succeeds, keep that permutation (call it the m-permutation) and construct str1_s and str2_s from str1 and str2 by removing all symbols that don't have s units, then carry out the algorithm again for str1_s and str2_s. If construction fails, they are not equivalent. If it succeeds, the final permutation will be a combination of the m-permutation and the s-permutation (although you probably don't even need to construct it, you just care that it exists).
If you pass a string to SymPy's sympify() function, it will automatically create the Symbols for you (no need to define them all).
>>> from sympy import *
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> sympify("x**2 + cos(x)")
x**2 + cos(x)
>>> sympify("diff(x**2 + cos(x), x)")
2*x - sin(x)
I did it once, in one simulater of mathemathics estudies..
Well, in my case, i knew what were the variables that will be used.
So, i tested the result putting values inside the vars.
A = 10
B = 20
C = 30
m = Math.e
s = Math.pi
And so, we solve:
s1 = 'A m * B s / (A m + C m)'
s2 = 'C m * B s / (C m + A m)'
If s1 != s2, was proved there isn't equivalence
With this method is impossible say that two expressions are equivalent,
But you can say that both isn't equivalent
if s1 != s2:
print "Not equivalent"
else:
print "Try with another sample"
Well.. I hope that this can help you.
This, like all other answers to date is not a robust solution to the problem, but instead contains more helpful information for our future meticulous friend to solve it.
I provide a difficult example using Euler's Formula https://en.wikipedia.org/wiki/Euler%27s_formula
I am certain all other overflow answers to date will not succeed in my example.
I show that all the suggestions on sympy's website also fail on my example. (https://github.com/sympy/sympy/wiki/Faq)
#SOURCE FOR HELPERS: https://github.com/sympy/sympy/wiki/Faq
import sympy
import sympy.parsing.sympy_parser
ExampleExpressionString1 = 'exp( i*( (x0 - 1)*(x0 + 2) ) )'
ExampleExpressionSympy1 = sympy.parsing.sympy_parser.parse_expr(ExampleExpressionString1)
ExampleExpressionString2 = 'i*sin( (x0 - 1)*(x0 + 2) ) + cos( (x0 - 1)*(x0 + 2) )'
ExampleExpressionSympy2 = sympy.parsing.sympy_parser.parse_expr(ExampleExpressionString2)
print '(ExampleExpressionSympy1 == ExampleExpressionSympy2):'
print ' ', (ExampleExpressionSympy1 == ExampleExpressionSympy2)
print '(ExampleExpressionSympy1.simplify() == ExampleExpressionSympy2.simplify()):'
print ' ', (ExampleExpressionSympy1.simplify() == ExampleExpressionSympy2.simplify())
print '(ExampleExpressionSympy1.expand() == ExampleExpressionSympy2.expand()):'
print ' ', (ExampleExpressionSympy1.trigsimp() == ExampleExpressionSympy2.trigsimp())
print '(ExampleExpressionSympy1.trigsimp() == ExampleExpressionSympy2.trigsimp()):'
print ' ', (ExampleExpressionSympy1.trigsimp() == ExampleExpressionSympy2.trigsimp())
print '(ExampleExpressionSympy1.simplify().expand().trigsimp() == ExampleExpressionSympy2.simplify().expand().trigsimp()):'
print ' ', (ExampleExpressionSympy1.simplify().expand().trigsimp() == ExampleExpressionSympy2.simplify().expand().trigsimp())
MORE NOTES:
I suspect this is a difficult problem to solve generically, and robustly. To properly check mathematical equivalence, you not only have to try order permutations, but you also have to have a library of mathematical equivalent transformations and try all those permutations as well.
I do however believe this might be a solvable problem, because Wolfram Alpha seems to have 'alternate expression' section, which seems to do the trick of providing all permutations most of the time on arbitrary expressions using these kinds of equivalences.
IN SUMMATION:
I suggest the following with the expectation that it will break:
import sympy
import sympy.parsing.sympy_parser
Expression.simplify().expand().trigsimp()

Average of two strings in alphabetical/lexicographical order

Suppose you take the strings 'a' and 'z' and list all the strings that come between them in alphabetical order: ['a','b','c' ... 'x','y','z']. Take the midpoint of this list and you find 'm'. So this is kind of like taking an average of those two strings.
You could extend it to strings with more than one character, for example the midpoint between 'aa' and 'zz' would be found in the middle of the list ['aa', 'ab', 'ac' ... 'zx', 'zy', 'zz'].
Might there be a Python method somewhere that does this? If not, even knowing the name of the algorithm would help.
I began making my own routine that simply goes through both strings and finds midpoint of the first differing letter, which seemed to work great in that 'aa' and 'az' midpoint was 'am', but then it fails on 'cat', 'doggie' midpoint which it thinks is 'c'. I tried Googling for "binary search string midpoint" etc. but without knowing the name of what I am trying to do here I had little luck.
I added my own solution as an answer
If you define an alphabet of characters, you can just convert to base 10, do an average, and convert back to base-N where N is the size of the alphabet.
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def enbase(x):
n = len(alphabet)
if x < n:
return alphabet[x]
return enbase(x/n) + alphabet[x%n]
def debase(x):
n = len(alphabet)
result = 0
for i, c in enumerate(reversed(x)):
result += alphabet.index(c) * (n**i)
return result
def average(a, b):
a = debase(a)
b = debase(b)
return enbase((a + b) / 2)
print average('a', 'z') #m
print average('aa', 'zz') #mz
print average('cat', 'doggie') #budeel
print average('google', 'microsoft') #gebmbqkil
print average('microsoft', 'google') #gebmbqkil
Edit: Based on comments and other answers, you might want to handle strings of different lengths by appending the first letter of the alphabet to the shorter word until they're the same length. This will result in the "average" falling between the two inputs in a lexicographical sort. Code changes and new outputs below.
def pad(x, n):
p = alphabet[0] * (n - len(x))
return '%s%s' % (x, p)
def average(a, b):
n = max(len(a), len(b))
a = debase(pad(a, n))
b = debase(pad(b, n))
return enbase((a + b) / 2)
print average('a', 'z') #m
print average('aa', 'zz') #mz
print average('aa', 'az') #m (equivalent to ma)
print average('cat', 'doggie') #cumqec
print average('google', 'microsoft') #jlilzyhcw
print average('microsoft', 'google') #jlilzyhcw
If you mean the alphabetically, simply use FogleBird's algorithm but reverse the parameters and the result!
>>> print average('cat'[::-1], 'doggie'[::-1])[::-1]
cumdec
or rewriting average like so
>>> def average(a, b):
... a = debase(a[::-1])
... b = debase(b[::-1])
... return enbase((a + b) / 2)[::-1]
...
>>> print average('cat', 'doggie')
cumdec
>>> print average('google', 'microsoft')
jlvymlupj
>>> print average('microsoft', 'google')
jlvymlupj
It sounds like what you want, is to treat alphabetical characters as a base-26 value between 0 and 1. When you have strings of different length (an example in base 10), say 305 and 4202, your coming out with a midpoint of 3, since you're looking at the characters one at a time. Instead, treat them as a floating point mantissa: 0.305 and 0.4202. From that, it's easy to come up with a midpoint of .3626 (you can round if you'd like).
Do the same with base 26 (a=0...z=25, ba=26, bb=27, etc.) to do the calculations for letters:
cat becomes 'a.cat' and doggie becomes 'a.doggie', doing the math gives cat a decimal value of 0.078004096, doggie a value of 0.136390697, with an average of 0.107197397 which in base 26 is roughly "cumcqo"
Based on your proposed usage, consistent hashing ( http://en.wikipedia.org/wiki/Consistent_hashing ) seems to make more sense.
Thanks for everyone who answered, but I ended up writing my own solution because the others weren't exactly what I needed. I am trying to average app engine key names, and after studying them a bit more I discovered they actually allow any 7-bit ASCII characters in the names. Additionally I couldn't really rely on the solutions that converted the key names first to floating point, because I suspected floating point accuracy just isn't enough.
To take an average, first you add two numbers together and then divide by two. These are both such simple operations that I decided to just make functions to add and divide base 128 numbers represented as lists. This solution hasn't been used in my system yet so I might still find some bugs in it. Also it could probably be a lot shorter, but this is just something I needed to get done instead of trying to make it perfect.
# Given two lists representing a number with one digit left to decimal point and the
# rest after it, for example 1.555 = [1,5,5,5] and 0.235 = [0,2,3,5], returns a similar
# list representing those two numbers added together.
#
def ladd(a, b, base=128):
i = max(len(a), len(b))
lsum = [0] * i
while i > 1:
i -= 1
av = bv = 0
if i < len(a): av = a[i]
if i < len(b): bv = b[i]
lsum[i] += av + bv
if lsum[i] >= base:
lsum[i] -= base
lsum[i-1] += 1
return lsum
# Given a list of digits after the decimal point, returns a new list of digits
# representing that number divided by two.
#
def ldiv2(vals, base=128):
vs = vals[:]
vs.append(0)
i = len(vs)
while i > 0:
i -= 1
if (vs[i] % 2) == 1:
vs[i] -= 1
vs[i+1] += base / 2
vs[i] = vs[i] / 2
if vs[-1] == 0: vs = vs[0:-1]
return vs
# Given two app engine key names, returns the key name that comes between them.
#
def average(a_kn, b_kn):
m = lambda x:ord(x)
a = [0] + map(m, a_kn)
b = [0] + map(m, b_kn)
avg = ldiv2(ladd(a, b))
return "".join(map(lambda x:chr(x), avg[1:]))
print average('a', 'z') # m#
print average('aa', 'zz') # n-#
print average('aa', 'az') # am#
print average('cat', 'doggie') # d(mstr#
print average('google', 'microsoft') # jlim.,7s:
print average('microsoft', 'google') # jlim.,7s:
import math
def avg(str1,str2):
y = ''
s = 'abcdefghijklmnopqrstuvwxyz'
for i in range(len(str1)):
x = s.index(str2[i])+s.index(str1[i])
x = math.floor(x/2)
y += s[x]
return y
print(avg('z','a')) # m
print(avg('aa','az')) # am
print(avg('cat','dog')) # chm
Still working on strings with different lengths... any ideas?
This version thinks 'abc' is a fraction like 0.abc. In this approach space is zero and a valid input/output.
MAX_ITER = 10
letters = " abcdefghijklmnopqrstuvwxyz"
def to_double(name):
d = 0
for i, ch in enumerate(name):
idx = letters.index(ch)
d += idx * len(letters) ** (-i - 1)
return d
def from_double(d):
name = ""
for i in range(MAX_ITER):
d *= len(letters)
name += letters[int(d)]
d -= int(d)
return name
def avg(w1, w2):
w1 = to_double(w1)
w2 = to_double(w2)
return from_double((w1 + w2) * 0.5)
print avg('a', 'a') # 'a'
print avg('a', 'aa') # 'a mmmmmmmm'
print avg('aa', 'aa') # 'a zzzzzzzz'
print avg('car', 'duck') # 'cxxemmmmmm'
Unfortunately, the naïve algorithm is not able to detect the periodic 'z's, this would be something like 0.99999 in decimal; therefore 'a zzzzzzzz' is actually 'aa' (the space before the 'z' periodicity must be increased by one.
In order to normalise this, you can use the following function
def remove_z_period(name):
if len(name) != MAX_ITER:
return name
if name[-1] != 'z':
return name
n = ""
overflow = True
for ch in reversed(name):
if overflow:
if ch == 'z':
ch = ' '
else:
ch=letters[(letters.index(ch)+1)]
overflow = False
n = ch + n
return n
print remove_z_period('a zzzzzzzz') # 'aa'
I haven't programmed in python in a while and this seemed interesting enough to try.
Bear with my recursive programming. Too many functional languages look like python.
def stravg_half(a, ln):
# If you have a problem it will probably be in here.
# The floor of the character's value is 0, but you may want something different
f = 0
#f = ord('a')
L = ln - 1
if 0 == L:
return ''
A = ord(a[0])
return chr(A/2) + stravg_half( a[1:], L)
def stravg_helper(a, b, ln, x):
L = ln - 1
A = ord(a[0])
B = ord(b[0])
D = (A + B)/2
if 0 == L:
if 0 == x:
return chr(D)
# NOTE: The caller of helper makes sure that len(a)>=len(b)
return chr(D) + stravg_half(a[1:], x)
return chr(D) + stravg_helper(a[1:], b[1:], L, x)
def stravg(a, b):
la = len(a)
lb = len(b)
if 0 == la:
if 0 == lb:
return a # which is empty
return stravg_half(b, lb)
if 0 == lb:
return stravg_half(a, la)
x = la - lb
if x > 0:
return stravg_helper(a, b, lb, x)
return stravg_helper(b, a, la, -x) # Note the order of the args

Categories