Python - looking for a way to make this more efficient - python

I'm relatively new to python and I'm looking for a way to make this code more efficient. Thank you!
import random
import string
import time
ques = []
anss = []
nums = string.digits
def que():
for i in range(10):
num0 = random.choice(nums)
num1 = random.choice(nums)
ans = num0 + '+' + num1
ques.append(ans)
ans = int(num0) + int(num1)
anss.append(ans)
global length
length = len(ques)
def main():
global counter
counter = 0
for i in range(10):
global answer
answer = int(input('What is ' + ques[i] + '?\n>>'))
if answer == anss[i]:
print('Correct!')
counter += 1
else:
print('Wrong!')
def score():
x = str((counter / length*100))
x = 'You got ' + x + '%'
return x
if __name__ == '__main__':
que()
main()
time.sleep(0.5)
print('Please wait while we calculate your score.')
time.sleep(1)
print(score())
Before, I tried just having the answers and questions in a single list - but after one loop it'd add one to the index something like this.
for i in range(10):
if answer == questions[i+1]:
print('correct')
i += 1 #This is meant to skip the answer part of the list and goto the next question but i couldnt get it to work.

Well... I just change your score method like
def score():
return 'You got ' + str((counter / length*100)) + '%'
In order to return the full string and avoid the x variable.

It is a little longer but what I think cleaner.
You can test each part without main.
main contains the algorithm.
from random import choice
from string import digits
def main() -> None:
score = 0
questions_limit = 10
for i in range(questions_limit):
first_operand = get_random_operand()
second_operand = get_random_operand()
expected_answer = first_operand + second_operand
question = get_question(first_operand, second_operand)
actual_answer = get_actual_answer(question)
correct = is_correct(expected_answer, actual_answer)
print_result(correct)
score = get_score(score, correct)
score_percentage = get_score_percentage(score, questions_limit)
print_score(score_percentage)
def get_random_operand() -> int:
return int(choice(digits))
def get_question(first_operand: int, second_operand: int) -> str:
return 'What is ' + str(first_operand) + ' + ' + str(second_operand) + ' ?\n>>'
def get_actual_answer(question: str) -> int:
return int(input(question))
def is_correct(expected_answer: int, actual_answer: int) -> bool:
return expected_answer == actual_answer
def print_result(correct: bool) -> None:
message = "Correct!" if correct else "Wrong!"
print(message)
def get_score(score: int, correct: bool) -> int:
if correct:
score +=1
return score
def get_score_percentage(score, questions_limit) -> str:
return str(score / questions_limit * 100)
def print_score(score: str) -> None:
print('You got ' + score + '%')
if __name__ == '__main__':
main()

If you were wanting to have the questions and answers in the same array then I would suggest doing something like:
for i in range(0,10,2):
if answer == questions[i+1]:
print("correct")
However the length of the array should be made to match having a question for each answer. The 2 at the end of range will mean the iterator(i) goes up by 2 each loop

It does not directly answer your question, but I'd suggest that you use the global keyword much less:
For answer you don't need it at all, because you use answer nowhere outside of main.
For length I would instead suggest you define length = 10 below nums at the top of your code and then replace all occurrences of 10 by length (which I would rather call n_questions).
That way you can easily change the number of questions with one single change of your code, and as you know that the loop in que will run up to length, there is no need to count length up.
Having counter global in main in okayish, although many would argue that it'd be better practice to return counter in main and define score to accept counter (better n_correct, for example) as argument like this (reusing Alfa Rojos answer):
def score(counter):
return 'You got ' + str((counter / length*100)) + '%'

I didn't completely rewrite it, but I would move everything to one loop, much cleaner for changes that way and have a counter that counts the number the user gets correct.
Here is a partial rewrite of your function que
def que():
for i in range(10):
num0,num1 = int(random.choice(nums)), int(random.choice(nums))
ans = num0 + num1
ques.append(ans)
anss.append(ans)
length = len(ques)
return length

Related

Harvard CS50P PSET 4: timed out while waiting for program to exit

I'm solving this problem.
My code works when I test it but it is failing the provided check with the error:
:( Little Professor accepts valid level // timed out while waiting for program to exit
It passes the three previous checks for invalid levels. Any insight to why this might be happening would help.
Here is my code:
def main():
level = get_level()
q=1
s=0
while q<=10:
try:
x = generate_integer(level)
y = generate_integer(level)
ans = x + y
eq = int(input(f'{x} + {y} = '))
if ans == eq:
s += 1
pass
elif ans != eq:
c = 1
while c <= 2:
print('EEE')
eq = int(input(f'{x} + {y} = '))
if ans == eq:
s += 1
break
else:
c += 1
if c == 3:
print('EEE')
print(f'{x} + {y} = {ans}')
q += 1
except EOFError:
pass
print(f'Score: {s}')
def get_level():
valid_inputs = (1,2,3)
while True:
try:
level = int(input('Level: '))
if level in valid_inputs:
return level
else:
pass
except ValueError:
pass
def generate_integer(level):
max = 10**level-1
min = 10**(level-1)
if level == 1:
min == 0
num = random.randint(min,max)
return num
main()
I ran your program (as-is) and found at least 1 problem. I'm not sure if it's why check50 flags that error, but you will need to fix it before it will pass. I entered Level: 1, then got a duplicate equation (4 + 5 = ). This violates a requirement buried under How to Test: "Your program should output 10 distinct problems" (emphasis mine). It's easy to miss - I didn't see it until someone mentioned it on the CS50P ED Forum.
Also, the last 2 lines of your program are supposed to be:
if __name__ == "__main__":
main()
Instead, only you have:
main()
Finally, this is an observation on your code structure. You have a lot of if statements for the 3 attempts to answer the question. It's overly complicated and kind of hard to follow. Hint: It can be simplified with another loop to prompt and check answers.

How would I make it to where if I get the right answer it will print something

import random
from browser import timer
operators = ['*', '/', '+', '-']
number = input('How many problems would you like?')
number = int(number)
counter = 1
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {} {}'.format(first, randoperator, second, "= ")
answer = input(problem)
correct = problem
counter+=1
I have tried putting this in but it doesn't run anything
if problem == answer:
print("Correct!")
You need to actually do the calculation to find out what the answer is. Here's a quick and dirty way to do that:
import random
operators = ['*', '/', '+', '-']
number = input('How many problems would you like?')
number = int(number)
counter = 1
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {}'.format(first, randoperator, second)
answer = input(problem + ' = ')
if eval(problem) == float(answer):
print("Correct!")
counter+=1
Using eval is not a great idea for reasons outlined in the answers to this question. In your case, you already know the two integers and the operator, so finding the expected answer without eval is pretty easy. Say you define a function that can do this for you:
def arithmetic(op, a, b):
if op == "+":
return a + b
elif op == "-":
return a - b
elif op == "*":
return a * b
elif op == "/":
return a / b
Then call this function to get the expected answer and compare that with the answer that the user gave,
while counter <= number:
first = random.randint(0,10)
second = random.randint(0,10)
randoperator = random.choice(operators)
problem = '{} {} {}'.format(first, randoperator, second)
answer = input(problem + ' = ')
if arithmetic(randoperator, first, second) == float(answer):
print("Correct!")
counter+=1

Python brute force password guesser

I am doing a task in class about a password guesser. I stumbled into a lot of problems trying to solve this task, my first approach was to use for loops (code below), but I realized that the amount of 'for loops' is equal to the length of the string.
a_z = 'abcdefghijklmnopqrstuvwxyz'
pasw = 'dog'
tests = 0
guess = ''
azlen = len(a_z)
for i in range(azlen):
for j in range(azlen):
for k in range(azlen):
guess = a_z[i] + a_z[j] + a_z[k]
tests += 1
if guess == pasw:
print('Got "{}" after {} tests'.format(guess, str(tests)))
break
input()
The program above is very concrete. It only works if there are exactly 3 characters entered. I read that you could use a package called intertools, however, I really want to find another way of doing this. I thought about using recursion but don't even know where to start.
import string
import itertools
for possible_password in itertools.permutations(string.ascii_letters, 3):
print(possible_password)
If you don't want to use itertools you can certainly do this with recursion, which will work with passwords of any (reasonable) length—it's not wired to three characters. Basically, each recursive call will attempt to append a new character from your alphabet to your running value of guess. The base case is when the guess attains the same length as value you're seeking, in which case you check for a match. If a match is found, return an indication that you have succeeded (I used return True) so you can short circuit any further searching. Otherwise, return a failure indication (return False). The use of a global counter makes it a bit uglier, but produces the same results you reported.
ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
def brute_force_guesser(passwd, guess = ''):
global _bfg_counter
if len(guess) == 0:
_bfg_counter = 0
if len(guess) == len(passwd):
_bfg_counter += 1
if guess == passwd:
print('Got "{}" after {} tests'.format(guess, str(_bfg_counter)))
return True
return False
else:
for c in ALPHABET:
if brute_force_guesser(passwd, guess + c):
return True
return False
brute_force_guesser('dog') # => Got "dog" after 2399 tests
brute_force_guesser('doggy') # => Got "doggy" after 1621229 tests
One way to avoid the global counter is by using multiple return values:
ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
def brute_force_guesser(target, guess = '', counter = 0):
if len(guess) == len(target):
counter += 1
if guess == target:
print('Got "{}" after {} tests'.format(guess, str(counter)))
return True, counter
return False, counter
else:
for c in ALPHABET:
target_found, counter = brute_force_guesser(target, guess + c, counter)
if target_found:
return True, counter
return False, counter
brute_force_guesser('dog') # => Got "dog" after 2399 tests
brute_force_guesser('doggy') # => Got "doggy" after 1621229 tests
Here is my full answer, sorry if it's not neat, I'm still new to coding in general. The credit goes to #JohnColeman for the great idea of using bases.
import math
global guess
pasw = str(input('Input password: '))
chars = 'abcdefghijklmnopqrstuvwxyz' #only limeted myself to lowercase for simplllicity.
base = len(chars)+1
def cracker(pasw):
guess = ''
tests = 1
c = 0
m = 0
while True:
y = tests
while True:
c = y % base
m = math.floor((y - c) / base)
y = m
guess = chars[(c - 1)] + guess
print(guess)
if m == 0:
break
if guess == pasw:
print('Got "{}" after {} tests'.format(guess, str(tests)))
break
else:
tests += 1
guess = ''
cracker(pasw)
input()
import itertools
import string
def guess_password(real):
chars = string.ascii_lowercase + string.digits
attempts = 0
for password_length in range(1, 20):
for guess in itertools.product(chars, repeat=password_length):
attempts += 1
guess = ''.join(guess)
if guess == real:
return 'the password is {}, found in {} guesses.'.format(guess, attempts)
print(guess, attempts)
print(guess_password('abc'))

Trying to Make a Quiz

So I am trying to run a program on python3 that asks basic addition questions using random numbers. I have got the program running however, I wanted to know if there was a way I could count the occurrences of "Correct" and "Wrong" so I can give the person taking the quiz some feedback on how they did.
import random
num_ques=int(input('Enter Number of Questions:'))
while(num_ques < 1):
num_ques=int(input('Enter Positive Number of Questions:'))
for i in range(0, (num_ques)):
a=random.randint(1,100)
b=random.randint(1,100)
answer=int(input(str(a) + '+' + str(b) + '='))
sum=a+b
if (answer==sum):
print ('Correct')
else:
print ('Wrong.')
You could use a dict to store counts for each type, increment them when you come across each type, and then access it after to print the counts out.
Something like this:
import random
stats = {'correct': 0, 'wrong': 0}
num_ques=int(input('Enter Number of Questions:'))
while(num_ques < 1):
num_ques=int(input('Enter Positive Number of Questions:'))
for i in range(0, (num_ques)):
a=random.randint(1,100)
b=random.randint(1,100)
answer=int(input(str(a) + '+' + str(b) + '='))
sum=a+b
if (answer==sum):
print ('Correct')
stats['correct'] += 1
else:
print ('Wrong.')
stats['wrong'] += 1
print "results: {0} correct, {1} wrong".format(stats['correct'], stats['wrong'])
import operator
import random
def ask_float(prompt):
while True:
try: return float(input(prompt))
except: print("Invalid Input please enter a number")
def ask_question(*args):
a = random.randint(1,100)
b = random.randint(1,100)
op = random.choice("+-/*")
answ = {"+":operator.add,"-":operator.sub,"*":operator.mul,"/":operator.div}[op](a,b)
return abs(answ - ask_float("%s %s %s = ?"%(a,op,b)))<0.01
num_questions = 5
answers_correct = sum(map(ask_question,range(num_questions)))
print("You got %d/%d Correct!"%(answers_correct,num_questions))

reprompting after invalid input

There was no problem until I tried to make an input go through validity check and if invalid ask again for input
i'm counting on you for ideas thanks in advance :)
a=0
def reinp(a,b):
while True:
if a in [1,2,3,4,5,6]: #checking for valid input
return int(a)
break
a=input(b)
else:
return print("error")
tried, not working either
def reinp(a,b):
for c in [1,2,3,4,5,6]:
if int(c)==int(a):
return int(a)
break
else:
a=input(b)
a=reinp(a,'Test: ')
This one is the first to make a problem
def reinp2(a,b): #trying to check if it's a number and can be turned to float if not ask again
while check(a):
a=input(b)
return float(a)
def check(a):
try:
float(a)
return False
except ValueError:
return True
Right now the problem is after the check it never breaks free from any while loop
i tried in place of while True:if...break,
while correct:
if... correct=False
didn't work
and it just asks again and again even a condition is met...
there is no raw_input in python 3.2 so i can't use that either
reinp2() is there so if there a solution found for reinp() the same could apply for reinp2() as well a and b are just variables ans[n]=reinp2(ans[n],"Input n: ") the same with reinp() just for another type of variable (one that can be float as well)
The code as it is now show no syntax errors
P.S. i'm using Python 3.2
[EDIT: Deleted original answer, since no longer relevant with fixed formatting on question]
The problem with reinp is that a will be a string, and you're checking it against integers.
...so change:
if a in [1,2,3,4,5,6]: #checking for valid input
to:
if a in ['1','2','3','4','5','6']: #checking for valid input
If you still have a problem with reinp2, perhaps you can show some code that demonstrates the issue. It looks fine to me.
P.S. It's complete i just wanted all of you who helped to know it's running without is any glitches i even customized it so it could receive initial data :) if someone need a permutation solver you know where to find it :)
If someone wants the script:
from math import *
ans=['n','k','choice',0,0,0,0,0]
n,k=0,1
a=['''1 For Permutations P (from n) = n
2 For Variations V (k emelments from n-th class) = n!/(n-k)!
3 For Combinations C (k emelments from n-th class) = n!/(k!(n-k)!) = ( n )
4 Use last answer. ( k )
5 Second Memory
6 Clear memory
Your choice is : ''',
'''+ to add
- to substract
* to multiply
/ to divide
You will undertake?: ''',
"The answer is: "]
def perm():
global ans
ans[n]=reinp2(ans[n],"Input n: ")
if ans[5]==0:
ans[3]=factorial(ans[n])
ans[6]=ans[3]
return print(a[2], ans[6])
else:
ans[4]=factorial(ans[n])
ans[6]=ops(ans[3],ans[4],ans[5])
return print(a[2], ans[6])
ans[n]=''
ans[k]=''
def var():
global ans
ans[n]=reinp2(ans[n],"Input n: ")
ans[k]=reinp2(ans[k],"Input k: ")
if ans[5]==0:
ans[3]=factorial(ans[n])/(factorial(ans[n]-ans[k]))
ans[6]=ans[3]
return print(a[2], ans[6])
else:
ans[4]=factorial(ans[n])/(factorial(ans[n]-ans[k]))
ans[6]=ops(ans[3],ans[4],ans[5])
return print(a[2], ans[6])
ans[n]=''
ans[k]=''
def comb():
global ans
ans[n]=reinp2(ans[n],"Input n: ")
ans[k]=reinp2(ans[k],"Input k: ")
if ans[5]==0:
ans[3]=factorial(ans[n])/((factorial(ans[n]-ans[k]))*(factorial(ans[k])))
ans[6]=ans[3]
return print(a[2], ans[6])
else:
ans[4]=factorial(ans[n])/((factorial(ans[n]-ans[k]))*(factorial(ans[k])))
ans[6]=ops(ans[3],ans[4],ans[5])
return print(a[2], ans[6])
ans[n]=''
ans[k]=''
def ent():
global ans,a
ans[2]=reinp(ans[2],a[0])
if ans[2]==5:
if ans[3]!=0:
ans[7]=ans[3]
print(ans[7])
ent()
if ans[2]==6:
clear()
print("Done!")
ent()
if ans[3]==0 and ans[2]==4:
print('The memory is empty...')
ent()
elif ans[3]!=0 and ans[2]==4:
ans[3]=ans[3]
ans[5]=reinp1(ans[5],a[1])
if ans[5] == '+' :
ans[5]='add'
print("Adding")
elif ans[5] == '-' :
ans[5]='sub'
print("Substracting")
elif ans[5] == '*' :
ans[5]='mul'
print("Multiplication")
elif ans[5] == '/' :
ans[5]='div'
print("Dividing")
ans[2]='choice'
ent()
if ans[2]==1:
perm()
elif ans[2]==2:
var()
elif ans[2]==3:
comb()
clear1()
ent()
def ops(a,b,c):
if c=='add':
return a+b
if c=='sub':
return a-b
if c=='mul':
return a*b
if c=='div':
return a/b
def reinp(a,b):
while True:
a=input(b)
if str(a) in ['1','2','3','4','5','6']:
return int(a)
break
else:
print('There was an error please try again:')
def reinp1(a,b):
while True:
a=input(b)
if a in ["+", "-", "*", "/"]:
return a
break
def reinp2(a,b):
while check2(a):
a=input(b)
return float(a)
def check2(a):
try:
float(a)
return False
except ValueError:
return True
def clear():
ans[0]='n'
ans[1]='k'
ans[2]='choice'
ans[3]=0
ans[4]=0
ans[5]=0
ans[7]=ans[6]
ans[6]=0
def clear1():
ans[0]='n'
ans[1]='k'
ans[2]='choice'
ent()

Categories

Resources