I'm trying to find a path in a game, you start out with value = 1 and you're supposed to add/multiply it with the right numbers in order until you get 49. For example, if I follow the path of 1+2*3, it'll be more like (1+2)*3 = 9 instead of 1+2*3 = 7.
I tried some code, like this:
result = 0
while result != 49:
values = ["+2","+3","+5","*2","*3","*5"]
pathway = random.sample(values, 5)
pathway = str(pathway)
pathway = pathway.replace("', '", "")
path = "1"+str(pathway)[2:-2]
result = eval(path)
print(path, "=", result)
I got my result pretty quickly, but when I tried it I realized that this function follows the order of operations (multiplication before addition). Is there an easy way to make it count from left to right without prioritising multiplication?
If what you want is to simply calculate 1+2*3 (or something) without order of operations, try this:
def do_math_left_to_right(expression):
pieces = []
i = 0
for c in expression:
if c in digits:
try:
pieces[i] += c
except IndexError:
pieces.append(c)
elif c in {'+', '-', '*', '/'}:
i += 1
pieces.append(c)
last_piece = pieces.pop(0)
result = 0
for piece in pieces:
result = eval(f'{last_piece}{piece}')
last_piece = str(result)
return result
This is a screenshot of the same function but with debugging messages:
Related
I am currently working on this problem called Uncompress:
Write a function, uncompress, that takes in a string as an argument. The input string will be formatted into multiple groups according to the following pattern:
<number><char>
for example, '2c' or '3a'.
The function should return an uncompressed version of the string where each 'char' of a group is repeated 'number' times consecutively. You may assume that the input string is well-formed according to the previously mentioned pattern.
And I hit a pretty interesting error on the last spec,
import unittest
from problems import uncompress
class TestUncompress(unittest.TestCase):
def test_uncompress(self):
string = "2c3a1t"
expected = "ccaaat"
actual = uncompress.uncompress(string)
self.assertEqual(expected, actual)
def test_uncompress_2(self):
string = "4s2b"
expected = "ssssbb"
actual = uncompress.uncompress(string)
self.assertEqual(expected, actual)
def test_uncompress_3(self):
string = "2p1o5p"
expected = "ppoppppp"
actual = uncompress.uncompress(string)
self.assertEqual(expected, actual)
def test_uncompress_4(self):
string = "3n12e2z"
expected = "nnneeeeeeeeeeeezz"
actual = uncompress.uncompress(string)
self.assertEqual(expected, actual)
def test_uncompress_5(self):
def helper(value):
return ''.join(['y' for _ in range(value)])
string = "127y"
expected = helper(127)
actual = uncompress.uncompress(string)
self.assertEqual(expected, actual)
Which is the error on the title, ValueError: invalid literal for int() with base 10: 'e'
I only get this error with this code,
def uncompress(string):
left = 0
right = 0
values = []
while right < len(string):
if string[left] == string[right]:
right += 1
else:
number = int(string[left: right])
values.append(number * string[right])
right += 1
left = right
return ''.join(values)
But if I do this, then the error goes away and it runs just fine.
def uncompress(string):
numbers = '123456789'
left = 0
right = 0
values = []
while right < len(string):
if string[right] in numbers:
right += 1
else:
number = int(string[left: right])
values.append(number * string[right])
right += 1
left = right
return ''.join(values)
So I am kind of confused with what the error is and what is generating it, from those 2 code snippets, the only thing I changed was the if statement, so I'm not sure what the issue is.
Thanks in advance!
Have you written the code yourself?
The second uncompress function is fine.
Now, it looks like your first uncompress function is rather a mix of a compress and uncompress, in particular the comparison if string[left] == string[right]: is really what one would do to check consecutive characters.
An indeed, only a few changes make it a compress function (aabbccc -> 2a2b3c):
def compress(string):
left = 0
right = 0
values = []
while right < len(string):
if string[left] == string[right]:
right += 1
else:
number = len(string[left: right]) ## changed
values.extend([str(number), string[left]]) ## changed
right += 1
left = right
number = len(string[left: right]) ## changed
values.extend([str(number), string[left]]) ## changed
return ''.join(values)
example:
>>> compress('zzyyyy')
2z3y
I am trying to remake the built-in function for bin(x) for better understanding, I have got that part down, now the issue is how to dynamically remove the 0s when they are not necessary.
I have tried using replace() but it seems to be removing every suggested "0" I am unsure how to select the zeroes till it hits the first index in which there is a "1"
for eg:
if i have 0b00010010
___
0b00010010
^
I would like to select the numbers after the 0b and erase the 0s right after until "1"
def bin(x):
if x>0:
binary = ""
i = 0
while x>0 and i<=16:
string = str(int(x%2))
binary = binary+string
x/=2
i = i+1
d = binary[::-1]
ret = f"0b{d}"
return ret.replace("00","")
else:
x = abs(x)
binary = ""
i = 0
while x > 0 and i <=16:
string = str(int(x % 2))
binary = binary + string
x /= 2
i = i + 1
nd = binary[::-1]
ret = f"-0b{nd}"
return ret.replace("00","")
print(bin(8314))# 0b00010000001111010 this is the current out
0b00010000001111010 this is the current output
0b10000001111010 this is what I want
It might be better to simplify things by not generating those extra zeroes in the first place:
def bin(x):
prefix = ("-" if x < 0 else "")
x = abs(x)
bits = []
while x:
x, bit = divmod(x, 2) # division and remainder in one operation
bits.append(str(bit))
# Flip the bits so the LSB is on the right, then join as string
bit_string = ''.join(bits[::-1])
# Form the final string
return f"{prefix}0b{bit_string}"
print(bin(8314))
prints
0b10000001111010
You should take a look at lstrip():
>>> b = "00010000001111010"
>>> b.lstrip("0")
'10000001111010'
Of course, make sure to prefix the binary with "0b" after calling lstrip().
Scott Hunter brought up a nice solution to your problem, however, if you want to use a for loop, consider trying the following:
binary = "0b00010000001111010"
start_index = binary.find("b")
for index in range(b+1, len(binary)):
if binary[index] == 0:
binary = binary[0:index:] + binary[index+1::]
else:
break
Hey guys i have a trouble when i want to add two binaries numbers in Python, i mean i can enter a chain of character in a form of a string but i don't know how to select a specific value in the chain. Here is my code:
chaina = input('Enter your first binary number')
chainb = input('Enter your second binary number')
liste = str()
r = 0
for i in range [-1,chaina]:
t = 0
t = chaina() + chainb() + r
if t == 2 :
r = 1
liste = str(t) + liste
elif t == 0 or t == 1:
r = 0
liste = str(t) + liste
To add two binary numbers chaina and chainb:
bin(eval('0b{} + 0b{}'.format(chaina, chainb)))
Or, if you want the binary number without the leading '0b':
format(eval('0b{} + 0b{}'.format(chaina, chainb)), 'b')
Explanation
Assume for illustration that chaina = '1010' and chainb = '1111'. Then:
>>> '0b{} + 0b{}'.format(chaina, chainb)
'0b1010 + 0b1111'
By applying eval() on this string, we get the same result as if we typed the expression 0b1010 + 0b1111 directly into Python console.
>>> 0b1010 + 0b1111
25
>>> eval('0b1010 + 0b1111')
25
Finally, bin() produces a binary representation of the number passed to it as an argument:
>>> bin(25)
'0b11001'
The same thing is accomplished by calling format() with a 'b' argument:
>>> format(25, 'b')
'11001'
All put together, we are getting the expressions shown above.
Why don't you simply convert them into decimal and add them as you would do with decimals:
y = '0b101010'
z = '0b101010'
print(int(y,2) + int(z,2))
print(bin((int(y,2) + int(z,2))))
Assuming that you want to do a binary sum by hand, you must:
process both numbers starting from the end (reversed will help here)
consistently add bits processing carry until the lengther of both numbers is exhausted
reorder the result bits (here again reversed)
Code could be (assuming that you can be sure that chaina and chainb only consist in 0 and 1 characters, no test for it here):
def binsum(chaina, chainb):
def next0(it):
"""Retrieve next digit from a binary representation, 0 when exhausted"""
try:
return int(next(it))
except StopIteration:
return 0
a = reversed(chaina) # reverse chains to start with lowest order bit
b = reversed(chainb)
r = 0
result = [] # future result
for i in range(n):
t = next0(a) + next0(b) + r # add with carry
if t > 1:
t -= 2
r = 1
else:
r = 0
result.append('1' if t else '0')
if r != 0: # do not forget last carry
result.append('1')
return ''.join(result)
A couple of suggestions
normalize the lengths of the bit strings
l0, l1 = map(len, (str0, str1))
if l0 < l1:
str0 = "0"*(l1-l0) + str0
elif l1 < l0:
str1 = "0"*(l0-l1) + str1
do a loop on the reversed strings elements and construct the binary string bottom up
remainder = 0
result = ""
for bit_0, bit1 in zip(reversed(str0), reversed(str1)):
bit_0, bit_1 = map(int, (bit_0, bit_1))
new_bit, remainder = f(bit_0, bit_1, remainder)
result = str(new_bit) + result
if remainder != 0
...
writing f(bit_0, bit_1, remainder) and treating what to do if remainder is not null at the end of the loop is left as an exercise.
I wrote the piece of code below a while back, and had this issue then as well. I ignored it at the time and when I came back to it after asking an 'expert' to look at it, it was working fine.
The issue is, sometimes the program seems unable to run the main() on my laptop, possibly due to how heavy the algorithm is. Is there a way around this? I would hate to keep having this problem in the future. The same code is working perfectly on another computer which i have limited access to.
(P.S. laptop having the issue is a MacBook Air 2015 and it should have no problem running the program. Also, it stops after printing "hi")
It does not give and error message, it just doesn't print anything from main(). It's supposed to print a series of strings which progressively converge to "methinks it is like a weasel". In eclipse, it shows that the code is still being processed but it does not output anything that it is supposed to
import random
def generateOne(strlen):
alphabet = "abcdefghijklmnopqrstuvwxyz "
res = ""
for i in range(strlen):
res = res + alphabet[random.randrange(27)]
return res
def score(goal, teststring):
numSame = 0
for i in range(len(goal)):
if goal[i] == teststring[i]:
numSame = numSame + 1
return numSame / len(goal)
def main():
goalstring = "methinks it is like a weasel"
chgoal = [0]*len(goalstring)
newstring = generateOne(28)
workingstring = list(newstring)
countvar = 0
finalstring = ""
while score(list(goalstring), workingstring) < 1:
if score(goalstring, newstring) > 0:
for j in range(len(goalstring)):
if goalstring[j] == newstring[j] and chgoal[j] == 0:
workingstring[j] = newstring[j]
chgoal[j] = 1
finalstring = "".join(workingstring)
countvar = countvar + 1
print(finalstring)
newstring = generateOne(28)
finalstring = "".join(workingstring)
print(finalstring)
print(countvar)
print("hi")
if __name__ == '__main__':
main()
print("ho")
You can optimize a bit. Strings are immutable - every time you append one char to a string a new string is created and replaces the old one. Use lists of chars instead - also do not use "".join() all the time for printing purposes if you can print the list of chars by decomposing and a seperator of "":
import random
def generateOne(strlen):
"""Create one in one random-call, return as list, do not iterativly add to string"""
alphabet = "abcdefghijklmnopqrstuvwxyz "
return random.choices(alphabet,k=strlen)
def score(goal, teststring):
"""Use zip and generator expr. for summing/scoring"""
return sum(1 if a==b else 0 for a,b in zip(goal,teststring))/len(goal)
def main():
goalstring = list("methinks it is like a weasel") # use a list
newstring = generateOne(28) # also returns a list
workingstring = newstring [:] # copy
countvar = 0
while score(goalstring, workingstring) < 1:
if score(goalstring, newstring) > 0:
for pos,c in enumerate(goalstring): # enumerate for getting the index
# test if equal, only change if not yet ok
if c == newstring[pos] and workingstring[pos] != c:
workingstring[pos] = newstring[pos] # could use c instead
countvar += 1
print(*workingstring, sep="") # print decomposed with sep of ""
# instead of "".join()
newstring = generateOne(28)
finalstring = "".join(workingstring) # create result once ...
# although its same as goalstring
# so we could just assing that one
print(finalstring)
print(countvar)
print("hi")
if __name__ == '__main__':
s = datetime.datetime.now()
main()
print(datetime.datetime.now()-s)
print("ho")
Timings with printouts are very unrelieable. If I comment the print printing the intermediate steps to the final solution and use a `random.seed(42)' - I get for mine:
0:00:00.012536
0:00:00.012664
0:00:00.008590
0:00:00.012575
0:00:00.012576
and for yours:
0:00:00.017490
0:00:00.017427
0:00:00.013481
0:00:00.017657
0:00:00.013210
I am quite sure this wont solve your laptops issues, but still - it is a bit faster.
I have the following code for example:
n = ['321','243','780']
b = ['12','56','90']
a = ['178', '765', '111']
E = input('Enter Word...')
qw = 1
Code = ('')
E_ready = [E[no:no+qw] for no in range(0, len(E), qw)]
for code in E_Ready:
letter = random.choice(code)
Code += letter
If you enter the word 'nba' then it will output as 'nba', I want it to output with random elements from each letter's respective list so for example '32112178'
As Willem van Onsem correctly mentioned in the comments:
"...that is really bad design, call-by-name is rather unsafe. You better use a dictionary."
So, try this:
n = {'option1':'321','option2':'243','option3':'780'}
letter = random.choice(list(n.values()))
Or, shorter, as Chris has mentioned:
d = {'n':[321, 243, 780]}
letter = random.choice(d['n'])
Results from print(letter) (on both options):
321
321
780
243
etc..
EDIT:
How to add extra variables:
n = 'n'
d = {n:[321, 243, 780]}
letter = random.choice(d[n])
q = 'q'
d[q] = [600, 234, 180]
new_letter = random.choice(d[q])
Now print(new_letter) gives:
234
180
180
600
etc..
SECOND EDIT (which btw is pure bonus since the question turned into a completely different one then first asked.. therefor, it is left unoptimized. Yet, working nonetheless..):
import random
d = {'n':[321, 243, 780], 'b':['12','56','90'], 'a':['178', '765', '111']}
E = input('Enter Word...')
inputword = list(E)
for key in d.keys():
if key in inputword:
for i in range(len(inputword)):
if inputword[i] == key:
try:
inputword[i] = str(random.choice(d[key]))
except:
pass
result = ''.join(inputword)
print(result)
If input = nba then output = 32190111
If input = nhhsygbbvvra then output = 321hhsyg5690vvr178
Etc..
Okay, you have a few fundamental issues.
When you want to assign one variable to another then you wouldn't put it in quotes.
So it should be:
code = n
But actually I'm wondering why you need the variable code at all.
You could simply do
import random
code = [ '321', '243', '780' ]
letter = random.choice(code)
print letter
I agree with the comment, it is better to use something like:
d = {'n':[mylist]}
letter = random.choice(d['n'])
the problem is that random.choice works on strings too... it simply considers them to be a list of characters. in your case the code variable and n are supposed to be exactly the same but are in fact not. You could also do
n = [list]
code = n ## only makes any sense, if there
## is a procedure assigns it based on some condition
letter = random.choice(code)