Genetic algorithm suspends in python? - python

I have implemented a simple genetic algorithm in python - here is the most of the code:
import random
ings = (('w1', 200, 25, 80),
('su1', 50, 55, 150),
('su2', 400, 100, 203),
('sy1', 10, 150, 355),
('sy2', 123, 88, 101),
('sy3', 225, 5, 30),
('sy4', 1, 44, 99),
('sy5', 500, 220, 300))
mutationRate = 0.2
crossoverRate = 0.9
iterations = 100
file = open('D:\\logfile2.txt', 'a')
class Ingredient:
def __init__(self, n, p, mi, ma):
self.name = n
self.price = p
self.min = mi
self.max = ma
self.perc = random.randrange(self.min, self.max)
class Drink:
def __init__(self):
self.ing = [Ingredient(*x) for x in ings]
self.normalize()
self.fitness = self.evaluate()
def normalize(self):
sum = 0
for x in self.ing:
sum += x.perc
if sum < 1000:
offset = 1000 - sum
while not offset == 0:
index = random.randrange(len(self.ing))
val = self.ing[index].max - self.ing[index].perc
threshold = random.randrange(val) if val > 0 else 0
threshold = threshold if threshold < offset else offset
self.ing[index].perc += threshold
offset -= threshold
if sum > 1000:
offset = sum - 1000
while not offset == 0:
index = random.randrange(len(self.ing))
val = self.ing[index].perc - self.ing[index].min
threshold = random.randrange(val) if val > 0 else 0
threshold = threshold if threshold < offset else offset
self.ing[index].perc -= threshold
offset -= threshold
def evaluate(self):
fitness = 0
for x in self.ing:
fitness += x.perc * x.price
return 300000 - fitness
class GeneticAlgorithm:
def __init__(self):
self.drinkList = [Drink() for x in range(8)]
self.pool = []
def mutate(self, index):
ing1, ing2 = random.randrange(8), random.randrange(8)
while ing1 == ing2:
ing2 = random.randrange(8)
ptr = self.drinkList[index].ing
ing1thr = ptr[ing1].max - ptr[ing1].perc
ing2thr = ptr[ing2].perc - ptr[ing2].min
if ing1thr & ing2thr:
change = random.randrange(ing1thr if ing1thr < ing2thr else ing2thr)
ptr[ing1].perc += change
ptr[ing2].perc -= change
def crossover(self, index1, index2):
ing1, ing2 = random.randrange(8), random.randrange(8)
while ing1 == ing2:
ing2 = random.randrange(8)
ptr1 = self.drinkList[index1].ing[:]
ptr2 = self.drinkList[index2].ing[:]
resultIndex1 = random.randrange(len(self.drinkList))
while True:
resultIndex2 = random.randrange(len(self.drinkList))
if not resultIndex1 == resultIndex2:
break
bias = 1 if ptr1[ing1].perc > ptr2[ing1].perc else -1
if bias == 1:
maxChange = min(ptr1[ing1].perc - ptr1[ing1].min,
ptr1[ing2].max - ptr1[ing2].perc,
ptr2[ing1].max - ptr2[ing1].perc,
ptr2[ing2].perc - ptr2[ing2].min)
if maxChange:
change = random.randrange(maxChange)
ptr1[ing1].perc -= change
ptr1[ing2].perc += change
ptr2[ing1].perc += change
ptr2[ing2].perc -= change
self.drinkList[resultIndex1].ing = ptr1[:]
self.drinkList[resultIndex2].ing = ptr2[:]
if bias == -1:
maxChange = min(ptr1[ing1].max - ptr1[ing1].perc,
ptr1[ing2].perc - ptr1[ing2].min,
ptr2[ing1].perc - ptr2[ing1].min,
ptr2[ing2].max - ptr2[ing2].perc)
if maxChange:
change = random.randrange(maxChange)
ptr1[ing1].perc += change
ptr1[ing2].perc -= change
ptr2[ing1].perc -= change
ptr2[ing2].perc += change
self.drinkList[resultIndex1].ing = ptr1[:]
self.drinkList[resultIndex2].ing = ptr2[:]
def roulette(self):
sum = 0
lst = []
for x in self.drinkList:
sum += x.fitness
lst.append(sum)
return lst
def selectOne(self):
selection = random.randrange(self.pool[-1])
index = 0
while selection >= self.pool[index]:
index += 1
return index
def selectCouple(self):
selection1 = random.randrange(self.pool[-1])
index1, index2 = 0, 0
while selection1 >= self.pool[index1]:
index1 += 1
while True:
selection2 = random.randrange(self.pool[-1])
while selection2 >= self.pool[index2]:
index2 += 1
if not index1 == index2: break
return (index1, index2)
def save(self, text):
file.write(text)
for x in self.drinkList:
for y in x.ing:
file.write('min: ' + str(y.min) +
' max: ' + str(y.max) +
' value: ' + str(y.perc) + '\n')
file.write('\n\n')
file.write('\nPopulation fitness: ' +
str(self.calculatePopulationFitness()) +
'\n\n----------------------------------------------\n\n')
def run(self):
file.write("Genetic algorithm\n\nAttributes values:\n" +
"Mutation rate: " + str(mutationRate) +
"\nCrossover rate: " + str(crossoverRate) +
"\nIterations: " + str(iterations) +
"\nIngredients:\n\n" + str(ings))
self.save('\n\n--First population--\n\n')
for cnt in range(iterations):
self.updateFitness()
self.pool = self.roulette()
if random.random() < mutationRate:
index = self.selectOne()
self.showFitness('Mutation in iteration ' + str(cnt))
self.mutate(index)
self.updateFitness()
self.showFitness('Results: ')
if random.random() < crossoverRate:
index1, index2 = self.selectCouple()
self.showFitness('Crossover in iteration ' + str(cnt))
self.crossover(index1, index2)
self.updateFitness()
self.showFitness('Results: ')
self.save('--Final population--\n\n')
def calculatePopulationFitness(self):
sum = 0
for x in self.drinkList:
sum += x.fitness
return sum
def updateFitness(self):
for x in self.drinkList:
x.fitness = x.evaluate()
def showFitness(self, text):
lst = [x.fitness for x in self.drinkList]
all = sum(lst)
file.write(text + '\n' + str(lst) + '||' + str(all) + '\n')
To run it I create an instance of GeneticAlgorithm and launch it through run() method.
The problem is, for low level of iterations the program works more or less fine, but if I set iteration to 50 for example, it seems to fall in infinite loop or suspend at random iteration (the logfile is not updated anymore and the program does not stop - happenes at random iteration). What can be the cause of this?
PS: Can you suggest any changes to the coding style? I'm quite new to python and i don't know all the conventions yet.

I don't completely understand your algorithm but it looks like your code hangs in this loop here:
while True:
selection2 = random.randrange(self.pool[-1])
while selection2 >= self.pool[index2]:
index2 += 1
if not index1 == index2: break
It gets to a point where you never get a value where index1 != index2. This could either indicate you have a mistake somewhere in your code, or that there isn't a situation that meets this condition. You could try putting a cap on the number of iterations of this, for example:
iters = 0
while iters < 5000:
selection2 = random.randrange(self.pool[-1])
while selection2 >= self.pool[index2]:
index2 += 1
iters += 1
if index1 != index2: break
if iters == 5000:
# Deal with not being able to identify a Couple

I know the question is more than a year old. Still I wanted a GA code in python to start with and found the problem.
while True:
selection2 = random.randrange(self.pool[-1])
while selection2 >= self.pool[index2]:
index2 += 1
if not index1 == index2: break
The problem is in this loop. once index2 is found to be equal it is not reset back to zero before trying to find a new value.
while True:
index2 = 0
selection2 = random.randrange(self.pool[-1])
while selection2 >= self.pool[index2]:
index2 += 1
if not index1 == index2: break

Related

N Puzzle with Depth First Search

I'm trying solve N Puzzle with Depth First Search using python 3.
With 3 x 3 puzzle it run good and fast but with 4 x 4 puzzle, it runs too slow and can't find solution with error: "MemoryError".
I also use "h(n) = depth + number of wrong tiles" to evaluate priority of each node.
I'm a newbie to python so hope you can help me with this
Here is my code:
import sys
import getopt
import random
import time
class State:
def __init__(self, parent, board, move, depth):
self.parent = parent
self.previousMove = move
self.board = board
self.map = ''.join(str(e) for e in board)
self.depth = depth
self.cost = self.calculateCost()
def calculateCost(self):
pos = 1
count = 0
for tile in self.board:
if tile == pos:
count += 1
pos += 1
return self.depth + 8 - count
class Puzzle:
def __init__(self, k, customBoard = None):
self.k = k
self.n = k*k - 1
self.sizeOfBoard = k*k
self.timeOfSolving = 0
self.timeOfGenerateSuccessors = 0
self.maxDeepSearch = 0
self.inititalState = State(None, self.createInitialBoard(customBoard), 'Start', 0)
self.goalBoard = self.createGoalBoard()
self.finalState = None
self.stateStorage = set() # Store states that have visited
self.path = [] # Store states that lead to goalstate
self.stack = []
def isSolvable(self, board):
# count invertion in puzzle's board
invCount = 0
for i in range(0, self.sizeOfBoard - 1):
if board[i] == 0:
continue
for j in range(i+1, self.sizeOfBoard):
if board[j] == 0:
continue
if board[i] > board[j]:
invCount += 1
# print(invCount)
if (invCount % 2 == 0):
return True
return False
def createInitialBoard(self, customBoard):
print("Creating initial state")
if customBoard is None:
board = []
lstAddSuccess = []
while 1:
board.clear()
lstAddSuccess.clear()
for count in range(0, self.k*self.k):
newTile = random.randint(0, self.n)
while newTile in lstAddSuccess:
newTile = random.randint(0, self.n)
lstAddSuccess += [newTile]
board += [newTile]
if self.isSolvable(board):
break
else:
board = [int(e) for e in customBoard]
if not self.isSolvable(board):
print("Cant find solution with this puzzle! Exiting...")
exit(-1)
return board
def createGoalBoard(self):
board = []
for count in range(1, self.n + 1):
board += [count]
board += [0]
return board
def printBoard(self, board):
for row in range(0, self.sizeOfBoard, self.k):
# for col in range(row, row + self.k):
print(board[row:row + self.k])
def generateSuccessors(self, currentState):
indexOfZero = currentState.board.index(0)
rowIndexOfZero = indexOfZero % self.k
colIndexOfZero = indexOfZero // self.k
lstSuccessors = []
# Slide to zero to up
if colIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - self.k]
newState[indexOfZero - self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'up', currentState.depth + 1))
# Slide zero to down
if colIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + self.k]
newState[indexOfZero + self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'down', currentState.depth + 1))
# slide zero to left
if rowIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - 1]
newState[indexOfZero - 1] = 0
lstSuccessors.append(
State(currentState, newState, 'left', currentState.depth + 1))
# Slide zero to right
if rowIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + 1]
newState[indexOfZero + 1] = 0
lstSuccessors.append(
State(currentState, newState, 'right', currentState.depth + 1))
lstSuccessorsCost = [ele.cost for ele in lstSuccessors]
lstSuccessorsInOrderOfCost = []
for i in range(0, len(lstSuccessorsCost)):
lstSuccessorsInOrderOfCost.append(lstSuccessors[lstSuccessorsCost.index(min(lstSuccessorsCost))])
lstSuccessorsCost[lstSuccessorsCost.index(min(lstSuccessorsCost))] = 100
return lstSuccessorsInOrderOfCost
def solvePuzzle(self, currentState):
self.stack.append(currentState)
self.stateStorage.add(currentState.map)
while len(self.stack) > 0:
currentState = self.stack.pop()
if currentState.board == self.goalBoard:
# find path
# self.printBoard(currentState.board)
self.finalState = currentState
print("Solving " + str(self.n) + " puzzle done!")
return
start_time_gen = time.time()
lstSuccessor = self.generateSuccessors(currentState)
end_time_gen = time.time()
timeOfGen = end_time_gen - start_time_gen
self.timeOfGenerateSuccessors += timeOfGen
for successor in lstSuccessor[::-1]:
if successor.map not in self.stateStorage:
self.stack.append(successor)
self.stateStorage.add(successor.map)
if successor.depth > self.maxDeepSearch:
self.maxDeepSearch += 1
print("Cant solve puzzle! Exiting...")
exit(-1)
def solve(self):
start_time = time.time()
self.solvePuzzle(self.inititalState)
end_time = time.time()
self.timeOfSolving = end_time - start_time
print("Running time: " + str(self.timeOfSolving))
print("Max Search Dept: " + str(self.maxDeepSearch))
print("Final State Dept: " + str(self.finalState.depth))
def printInitialBoard(self):
self.printBoard(self.inititalState.board)
def printPath(self):
if self.finalState is None:
print("No solution found!")
return
path = []
state = self.finalState
while (state is not None):
if state.previousMove is not None:
path.append(state.previousMove)
state = state.parent
print("path: "),
print(path[::-1])
def main(argv):
# if (len(argv) != 1 or int(argv[0]) not in range(1, 10000)):
# print("Input must be k of integer, which is k*k matrix of puzzle")
# exit()
# eight_puzzle = Puzzle(int(argv[0]))
k = int(input("Enter size of k * k puzzle, k = "))
while k not in range(2, 100):
print("k must be in range 2 - 100")
k = int(input("Enter size of k * k puzzle, k = "))
print("""
Choose:
1. Randome puzzle
2. Custome puzzle
""")
file = input()
if int(file) == 1:
puzzle = Puzzle(k)
elif int(file) == 2:
board = input("Enter puzzle: ")
puzzle = Puzzle(k ,list(board.split(" ")))
puzzle.printInitialBoard()
puzzle.solve()
puzzle.printPath()
if __name__ == "__main__":
main(sys.argv[1:])

Best way to run a function in main function when one of the parameters is not in the scope

Basically my brain is not working right now can't figure out the best way to resolve this error. builtins.NameError: name 'numIters' is not defined
I know this problem is that numIters is not defined in its scope but don't know the best solution to fix that.
Here is the bulk of my code
import random
alg = int(input("Select the sorting algorithm \n 1 - linear search \n 2 - binary search \nEnter Choice: "))
n = int(input("Choose the size of the list: "))
def main():
createList(n,alg)
print(runTest(values,n,alg))
printResults(alg,n,numIters)
#print(linearSearch(values,2))
#print(binarySearch(values, 2))
def createList(n,alg):
global values
values = []
random.seed(1456)
for j in range(n):
values.append(random.randint(0, 2*n))
while len(values) == n:
if alg == 2:
values.sort()
print(values)
return values
elif alg == 1:
print(values)
return values
def linearSearch(values, target):
numIters = 0
for i in range(len(values)):
numIters = numIters + 1
if values[i] == target:
return numIters
return -1
def binarySearch(values, target):
numIters = 0
start = 0
high = len(values) - 1
while start <= high:
middle = (start + high)//2
if values[middle] == target:
numIters = numIters + 1
return numIters
elif values[middle] > target:
numIters = numIters + 1
high = middle - 1
else:
numIters = numIters + 1
start = middle + 1
return -1
def runTest(values,n,alg):
if alg == 2:
count = 0
for j in range(n * 2):
count = count + 1
tgt = random.randint(0, 2*n)
binarySearch(values, tgt)
return count
elif alg == 1:
count = 0
for j in range(n * 2):
count = count + 1
tgt = random.randint(0, 2*n)
linearSearch(values, tgt)
return count
def printResults(alg, n, numIters):
avgIter = n / numIters
if alg == 2:
algType = Binary
if alg == 1:
algType = Linear
print("Results \n n = %d \n %s = %f.2 " % (n,algtype,avgIter))
main()
Thank you in advance for any help given as I am still trying to learn and understand how python works as a whole.
You need to return numIters so that you can pass it to the next function. It looks like it currently gets returned from binarySearch and linearSearch to runTest, but it gets discarded there; just bubble it up like this (I'm going to add type annotations and comments to help me keep track of what's going on):
from typing import List, Tuple
def runTest(values: List[int], n: int, alg: int) -> Tuple[int, int]:
"""Returns count, numIters"""
numIters = 0 # default value in case n is so small we don't iterate
if alg == 2:
count = 0
for j in range(n * 2):
count = count + 1
tgt = random.randint(0, 2*n)
numIters = binarySearch(values, tgt)
return count, numIters
elif alg == 1:
count = 0
for j in range(n * 2):
count = count + 1
tgt = random.randint(0, 2*n)
numIters = linearSearch(values, tgt)
return count, numIters
raise ValueError("alg needs to be 1 or 2!")
Now in your main() you can do:
def main():
createList(n, alg)
count, numIters = runTest(values, n, alg)
print(count)
printResults(alg, n, numIters)

string index out of range list iteration

I am fairly new to python, I am not sure on how to fix a index string out of range. it happens right after the while loop when I want to send mylist[i][0] to formatting function. Any pointer on my code in general would be awesome!
def formatting(str1):
if str1 == '?':
return True
else:
return False
while(i <= len(mylist)):
val = formatting(mylist[i][0])
if val == True:
str1 = mylist[i]
str2 = mylist[i+1]
i = i + 2
format_set(str1, str2)
else:
if format == True:
if (margin + count + len(mylist[i])) <= width:
if (i == (len(mylist)-1)):
list2.append(mylist[i])
print(" " * margin + " ".join(list2))
break
list2.append(mylist[i])
count += len(mylist[i])
i += 1
else:
print(" " * margin + " ".join(list2))
list2 = []
count = 0
else:
temp_margin = margin
temp_width = width
width = 60
margin = 0
if (margin + count + len(mylist[i])) <= width:
if (i == (len(mylist)-1)):
list2.append(mylist[i])
print(" " * margin + " ".join(list2))
margin = temp_margin
width = temp_width
break
list2.append(mylist[i])
count += len(mylist[i])
i += 1
else:
print(" " * margin + " ".join(list2))
list2 = []
count = 0
change
i <= len(mylist)
to
i < len(mylist)
In the last iteration of the while loop, i is referring to the last value. Hence,
str2 = mylist[i+1]
is trying to reference a string outside the allowed range and you get an error.
EDIT: Also, as Wcrousse mentioned, the while (i <= len(...)) should be changed to i < len(...) because indexes go from 0 - (length-1).

Need help in adding binary numbers in python

If I have 2 numbers in binary form as a string, and I want to add them I will do it digit by digit, from the right most end. So 001 + 010 = 011
But suppose I have to do 001+001, how should I create a code to figure out how to take carry over responses?
bin and int are very useful here:
a = '001'
b = '011'
c = bin(int(a,2) + int(b,2))
# 0b100
int allows you to specify what base the first argument is in when converting from a string (in this case two), and bin converts a number back to a binary string.
This accepts an arbitrary number or arguments:
>>> def bin_add(*bin_nums: str) -> str:
... return bin(sum(int(x, 2) for x in bin_nums))[2:]
...
>>> x = bin_add('1', '10', '100')
>>> x
'111'
>>> int(x, base = 2)
7
Here's an easy to understand version
def binAdd(s1, s2):
if not s1 or not s2:
return ''
maxlen = max(len(s1), len(s2))
s1 = s1.zfill(maxlen)
s2 = s2.zfill(maxlen)
result = ''
carry = 0
i = maxlen - 1
while(i >= 0):
s = int(s1[i]) + int(s2[i])
if s == 2: #1+1
if carry == 0:
carry = 1
result = "%s%s" % (result, '0')
else:
result = "%s%s" % (result, '1')
elif s == 1: # 1+0
if carry == 1:
result = "%s%s" % (result, '0')
else:
result = "%s%s" % (result, '1')
else: # 0+0
if carry == 1:
result = "%s%s" % (result, '1')
carry = 0
else:
result = "%s%s" % (result, '0')
i = i - 1;
if carry>0:
result = "%s%s" % (result, '1')
return result[::-1]
Can be simple if you parse the strings by int (shown in the other answer). Here is a kindergarten-school-math way:
>>> def add(x,y):
maxlen = max(len(x), len(y))
#Normalize lengths
x = x.zfill(maxlen)
y = y.zfill(maxlen)
result = ''
carry = 0
for i in range(maxlen-1, -1, -1):
r = carry
r += 1 if x[i] == '1' else 0
r += 1 if y[i] == '1' else 0
# r can be 0,1,2,3 (carry + x[i] + y[i])
# and among these, for r==1 and r==3 you will have result bit = 1
# for r==2 and r==3 you will have carry = 1
result = ('1' if r % 2 == 1 else '0') + result
carry = 0 if r < 2 else 1
if carry !=0 : result = '1' + result
return result.zfill(maxlen)
>>> add('1','111')
'1000'
>>> add('111','111')
'1110'
>>> add('111','1000')
'1111'
It works both ways
# as strings
a = "0b001"
b = "0b010"
c = bin(int(a, 2) + int(b, 2))
# as binary numbers
a = 0b001
b = 0b010
c = bin(a + b)
you can use this function I did:
def addBinary(self, a, b):
"""
:type a: str
:type b: str
:rtype: str
"""
#a = int('10110', 2) #(0*2** 0)+(1*2**1)+(1*2**2)+(0*2**3)+(1*2**4) = 22
#b = int('1011', 2) #(1*2** 0)+(1*2**1)+(0*2**2)+(1*2**3) = 11
sum = int(a, 2) + int(b, 2)
if sum == 0: return "0"
out = []
while sum > 0:
res = int(sum) % 2
out.insert(0, str(res))
sum = sum/2
return ''.join(out)
def addBinary(self, A, B):
min_len, res, carry, i, j = min(len(A), len(B)), '', 0, len(A) - 1, len(B) - 1
while i>=0 and j>=0:
r = carry
r += 1 if A[i] == '1' else 0
r += 1 if B[j] == '1' else 0
res = ('1' if r % 2 == 1 else '0') + res
carry = 0 if r < 2 else 1
i -= 1
j -= 1
while i>=0:
r = carry
r += 1 if A[i] == '1' else 0
res = ('1' if r % 2 == 1 else '0') + res
carry = 0 if r < 2 else 1
i -= 1
while j>=0:
r = carry
r += 1 if B[j] == '1' else 0
res = ('1' if r % 2 == 1 else '0') + res
carry = 0 if r < 2 else 1
j -= 1
if carry == 1:
return '1' + res
return res
#addition of two binary string without using 'bin' inbuilt function
numb1 = input('enter the 1st binary number')
numb2 = input("enter the 2nd binary number")
list1 = []
carry = '0'
maxlen = max(len(numb1), len(numb2))
x = numb1.zfill(maxlen)
y = numb2.zfill(maxlen)
for j in range(maxlen-1,-1,-1):
d1 = x[j]
d2 = y[j]
if d1 == '0' and d2 =='0' and carry =='0':
list1.append('0')
carry = '0'
elif d1 == '1' and d2 =='1' and carry =='1':
list1.append('1')
carry = '1'
elif (d1 == '1' and d2 =='0' and carry =='0') or (d1 == '0' and d2 =='1' and
carry =='0') or (d1 == '0' and d2 =='0' and carry =='1'):
list1.append('1')
carry = '0'
elif d1 == '1' and d2 =='1' and carry =='0':
list1.append('0')
carry = '1'
else:
list1.append('0')
if carry == '1':
list1.append('1')
addition = ''.join(list1[::-1])
print(addition)
Not an optimal solution but a working one without use of any inbuilt functions.
# two approaches
# first - binary to decimal conversion, add and then decimal to binary conversion
# second - binary addition normally
# binary addition - optimal approach
# rules
# 1 + 0 = 1
# 1 + 1 = 0 (carry - 1)
# 1 + 1 + 1(carry) = 1 (carry -1)
aa = a
bb = b
len_a = len(aa)
len_b = len(bb)
min_len = min(len_a, len_b)
carry = 0
arr = []
while min_len > 0:
last_digit_aa = int(aa[len(aa)-1])
last_digit_bb = int(bb[len(bb)-1])
add_digits = last_digit_aa + last_digit_bb + carry
carry = 0
if add_digits == 2:
add_digits = 0
carry = 1
if add_digits == 3:
add_digits = 1
carry = 1
arr.append(add_digits) # will rev this at the very end for output
aa = aa[:-1]
bb = bb[:-1]
min_len -= 1
a_len_after = len(aa)
b_len_after = len(bb)
if a_len_after > 0:
while a_len_after > 0:
while carry == 1:
if len(aa) > 0:
sum_digit = int(aa[len(aa) - 1]) + carry
if sum_digit == 2:
sum_digit = 0
carry = 1
arr.append(sum_digit)
aa = aa[:-1]
else:
carry = 0
arr.append(sum_digit)
aa = aa[:-1]
else:
arr.append(carry)
carry = 0
if carry == 0 and len(aa) > 0:
arr.append(aa[len(aa) - 1])
aa = aa[:-1]
a_len_after -= 1
if b_len_after > 0:
while b_len_after > 0:
while carry == 1:
if len(bb) > 0:
sum_digit = int(bb[len(bb) - 1]) + carry
if sum_digit == 2:
sum_digit = 0
carry = 1
arr.append(sum_digit)
bb = bb[:-1]
else:
carry = 0
arr.append(sum_digit)
bb = bb[:-1]
else:
arr.append(carry)
carry = 0
if carry == 0 and len(bb) > 0:
arr.append(bb[len(bb) - 1])
bb = bb[:-1]
b_len_after -= 1
if carry == 1:
arr.append(carry)
out_arr = reversed(arr)
out_str = "".join(str(x) for x in out_arr)
return out_str

Calculator which parses user input

import string
# Strength of operations:
# -> [] (brackets)
# 6 -> ~ (negative)
# 5 -> #, $, & (average, maximum, minimum)
# 4 -> %, ! (modulo, factorial)
# 3 -> ^ (power)
# 2 -> *, / (multiplication, division)
# 1 -> +, - (addition, subtraction)
def BinaryOperation(exp, idx):
""" Gets an expression and an index of an operator and returns a tuple with (first_value, operator, second_value). """
first_value = 0
second_value = 0
#Get first value
idx2 = idx -1
if idx2 == 0:
first_value = exp[idx2:idx]
else:
while (idx2 > 0) and (exp[idx2] in string.digits):
idx2 -=1
if (exp[idx2] in ("-")) or (exp[idx2] in string.digits):#-5*3
first_value = exp[idx2:idx]
else:#%5*3
first_value = exp[idx2+1:idx]
#Get second value
idx2 = idx +1
if exp[idx+1] not in string.digits: #If there is something like 1*+5, second_sign will be +.
idx2 += 1 #idx2 will begin from the char after the sign.
while (idx2 < len(exp)) and (exp[idx2] in string.digits):
idx2 += 1
second_value = exp[idx+1:idx2]
return (first_value, exp[idx], second_value)
def UnaryOperation(exp, idx):
""" Gets an expression and an index of an operator and returns a tuple with (operator, value). """
#Get value
idx2 = idx+1
if exp[idx+1] not in string.digits: #If there is something like ~-5, second_sign will be -.
idx2 += 1 #idx2 will begin from the char after the sign.
while (idx2 < len(exp)) and (exp[idx2] in string.digits):
idx2 +=1
return (exp[idx], exp[idx+1:idx2])
def Brackets(exp):
idx = 0
while idx < len(exp):
if exp[idx] == "[":
#Brackets
close_bracket = exp.find("]")
if close_bracket == -1:
raise Exception("Missing closing bracket.")
exp_brackets = exp[idx+1:close_bracket]
value = str(solve(exp_brackets))
exp = exp.replace("[" + exp_brackets + "]", value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level6(exp)
def Level6(exp):
idx = 0
while idx < len(exp):
if exp[idx] in ("~"):
#Negative
sub_exp = UnaryOperation(exp, idx)
value = ~int(sub_exp[1])
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level5(exp)
def Level5(exp):
idx = 0
while idx < len(exp):
if exp[idx] in ("#", "$", "&"):
#Average, Maximum and Minimum
sub_exp = BinaryOperation(exp, idx)
first_value = int(sub_exp[0])
second_value = int(sub_exp[2])
if sub_exp[1] == "#":
value = (first_value + second_value)/2
if sub_exp[1] == "$":
value = first_value if first_value > second_value else second_value
if sub_exp[1] == "&":
value = first_value if first_value < second_value else second_value
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level4(exp)
def Level4(exp):
idx = 0
while idx < len(exp):
if exp[idx] in ("%","!"):
#Modulo and Factorial
if exp[idx] == "%":
sub_exp = BinaryOperation(exp, idx)
value = int(sub_exp[0]) % int(sub_exp[2])
if exp[idx] == "!":
sub_exp = UnaryOperation(exp, idx)
value = reduce(lambda x,y:x*y, range(1, int(sub_exp[1])+1))
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level3(exp)
def Level3(exp):
idx = 0
while idx < len(exp):
if exp[idx] in ("^"):
#Power
sub_exp = BinaryOperation(exp, idx)
value = int(sub_exp[0]) ** int(sub_exp[2])
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level2(exp)
def Level2(exp):
idx = 0
while idx < len(exp):
if exp[idx] in ("*", "/"):
#Multiplication and Division
sub_exp = BinaryOperation(exp, idx)
if sub_exp[1] == "*":
value = int(sub_exp[0]) * int(sub_exp[2])
if sub_exp[1] == "/":
value = int(sub_exp[0]) / int(sub_exp[2])
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return Level1(exp)
def Level1(exp):
idx = 0
while idx < len(exp):
if (exp[idx] in ("+", "-")) and (idx != 0):
#Addition and Subtraction
sub_exp = BinaryOperation(exp, idx)
if sub_exp[1] == "+":
value = int(sub_exp[0]) + int(sub_exp[2])
if sub_exp[1] == "-":
value = int(sub_exp[0]) - int(sub_exp[2])
value = str(value)
exp = exp.replace(''.join(sub_exp), value)
idx = 0 #The len has been changed, scan again.
idx += 1
return exp
def solve(exp):
exp = Brackets(exp)
return float(exp) if "." in exp else int(exp)
def remove_whitespace(exp):
""" Gets a string and removes all whitespaces and tabs """
exp = exp.replace(" ", "")
exp = exp.replace("\t", "")
return exp
while True:
exp = raw_input("")
exp = remove_whitespace(exp)
print solve(exp)
I have written this program after a lot of effort, and I was wondering about the efficiency of that solution and if it's neat.
So my question is, how plain is this program and is there any better way to rewrite it?
just for the point.
>>> eval(raw_input("input calculation: "))
input calculation: 1+1
2
>>> eval(raw_input("input calculation: "))
input calculation: (6*4^2)
26
>>> eval(raw_input("input calculation: "))
input calculation: (3/2.3)*4
5.2173913043478262
for an innocent program, you can use eval
but you really shouldn't use it ever. its only real use is confusing people, and being a fun novelty if you write programs fro yourself and decide you want a calculator.
there are many ways to write a calculator function.
try some of these other answers:
Python creating a calculator
Basic calculator program in python
python calculator program
If you want to check out some custom class-based evaluation engines in Python, these might help you:
Expression Evaluator (version 1 with source)
Math Evaluator (version 2 with source)
again = True
answer = ""
while again is True:
try:
expression = raw_input("Enter your expression: ")
found = False
oper = -1
operator1 = 0
operator2 = 0
while found==False:
if (expression.find("+")>0 and expression.find("+")<len(expression)-1):
found = True
oper = expression.find("+")
operator1 = float(expression[:oper])
operator2 = float(expression[oper+1:])
print "{} + {} = {}".format(operator1,operator2,operator1+operator2)
elif(expression.find("-")>0 and expression.find("-")<len(expression)-1):
found = True
oper = expression.find("-")
operator1 = float(expression[:oper])
operator2 = float(expression[oper+1:])
print "{} - {} = {}".format(operator1,operator2,operator1-operator2)
elif(expression.find("*")>0 and expression.find("*")<len(expression)-1):
found = True
oper = expression.find("*")
operator1 = float(expression[:oper])
operator2 = float(expression[oper+1:])
print "{} * {} = {}".format(operator1,operator2,operator1*operator2)
elif(expression.find("/")>0 and expression.find("/")<len(expression)-1):
found = True
oper = expression.find("/")
operator1 = float(expression[:oper])
operator2 = float(expression[oper+1:])
print "{} / {} = {}".format(operator1,operator2,operator1/operator2)
else:
oper = -1
found = False
print "Incorrect expression, please try again"
break
again = False
answer = raw_input("Try again?: ")
if(answer == "y" or answer=="yes" or answer =="Y" or answer == "YES"):
again = True
else:
again = False
print "Thank you for playing! See you next time."
break
except:
print "Failed, check your expression and try again"

Categories