what is the time complexity for that piece of code? - python-3.x

what is the time complexity of this code?
how can I improve this code?
import random
n=input('choose one h/t? ')
v=random.randint(0,1)
if n=='h':
if v==0:
print('Hurrah!!! You win. Result is Head.')
else:
print('alas!!! You lose. Result is Tell.')
elif n=='t':
if v==1:
print('hurrah!!! You win. Result is Tell.')
else:
print('alas!!! You lose. Result is Head.')

I suggest you:
either merge the h and v if to get one if only
either determine first if it’s a h or t case, outside of other if. Then, use a function to test the other parts.
Indeed, just refactor to use functions as you are repeating yourself.

Related

Palindrome coding and logic

When investigating if a string is Palindrome, cant i just take the reverse of the reversed string? whats wrong with my code?
def reverse(s):
return s[::-1]
def isPalindrome(s):
if (s == reverse(reverse(s))):
return True
else:
return False
reverse(reverse(s)) always equals s. You want to check whether s equals reverse(s):
def is_palindrome(s):
return s == reverse(s)
It depends on what you are aiming to do. If you just want to have the logic implemented, then your version would work (yet you still have to correct your bug there, it's if (s == reverse(s)) and not reversing the reversed version); however, if that's the task which was given to you in your coding interview, then you're not really doing it right, because of:
You're using an integral API call to reverse the string (and hence you're not implementing isPalindrome);
The time complexity of your version will be much worse than the
complexity of isPalindrome algorithm per se.

Random number for variable when called

I'm fairly new in programming and python in general. I already learned for, if's, and while, and I'm trying to make a pretty decent rpg/dungeon game. I'm wondering how do I make a variable have a random number when used for the "damage".
I used randint(a, b) for the variable but it is only random once.
This is a creature example:
name_slime = "Red Slime"
slime_hp = randint(100, 200)
slime_atk = randint(1, 2)
slime_crit = 60
slime_evade = 10
slime_exp = randint(1, 5)
slime_chance = randint(50, 60)
And these will get called on another function:
def battle(enemy_name, enemy_hp, enemy_atk, enemy_crit, enemy_evade, enemy_exp):# Battle sequence
while enemy_hp > 0:
global player_hp, potion_count
print(f"{enemy_name}'s Stats:")
print(f"HP: {enemy_hp} Power: {enemy_atk} Crit Chance: {enemy_crit}% Evade: {enemy_evade}%\n")
print("Your Stats:")
....
print("Action:\n1. Attack\n2. Parry\n3. Use Potion")
choice = input("> ")
if choice == "1":
....
elif choice == "2":
....
elif choice == "3":
....
print(f"You gained {enemy_exp} EXP")
exp_check(enemy_exp)
print(f"Your EXP is now {player_exp}/{exp_limit}")
P.S: I scraped a lot of things in this code because it's quite long for there is a lot of lines for the calculation.
Here's the full code if anyone can help:
https://pastebin.com/iFMZyY4z
I'll just take the exp for this case.
In the variable "slime_exp" it should give a number between 1 and 5. But when i tried fighting the creature multiple times in one run (not exiting the terminal) it always give the same amount of exp each time.
I'm thinking that the randint(1, 5) is defined when the script is run and not when the variable is used.
This happens for all variable that have randint()
How can I make it so that it will be random when the variable is used?
Using the choice method is an alternative that might be suitable for you:
slime_exp = range(1,6) # slime_exp is now a variable of 'range' type!
For more infomation about the range() function see range in Python.
And whenever you use the variable slime_exp you can use choice(slime_exp).
choice is a function that returns a pseudo-random value from a list, range, tuple, string. For more information see choice in Python.
Note that, to use choice you have to "import" it first by doing one of these options:
from random import * (not very advised)
import random (in this case you should use random. choice instead of just choice)
or from random import choice
And when you want to use the variable slime_exp, you just use choice(slime_exp) instead of slime_exp. Note that this may be a good option if you don't need to use the same value several times since the exact value of choice(slime_exp) is "pseudo-randomized" everytime you use it.
You can write a function to update the slime_exp variable and call it when needed, like below.
slime_exp = randint(1, 5)
def update_slime_exp():
global slime_exp
slime_exp = randint(1, 5)
update_slime_exp()
print(slime_exp)
However, changing the state of global variable is not a good programming practise. Instead get the random number as return value and use it when needed instead of storing it in a global variable.

Python: NameError: name "string" is not defined, not via input()

The function below checks to see if the first 9 digits of string (n) equate to the 10th character (an integer from 1-9 or X for 10).
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (eval(n[i])*(i+1))
if checkSum%11 == eval(n[9]) or (checkSum%11 == 10 and n[9] == 'X'): return True
else: return False
When I run the function for n='020103803X' I get an error:
NameError: name 'X' is not defined
I've searched for this problem and found that most people's issues were with input() or raw_input(), but as I am not using input(), I'm confused as to why I can't test if a character is a specific string. This is my first post as Python beginner, please tell if I'm breaking rules or what extra info I should include.
The problem is with your use of eval: eval('X') is the same as doing X (without the quotes). python sees that as a variable reference, and you have no variable named X.
There is no reason to use eval here. What are you hoping to accomplish? Perhaps you should be checking to see if the character is a digit?
if checkSum%11 == n[9].isdigit() or (checkSum%11 == 10 and n[9] == 'X'): return True
You're trying to get a response from
eval('X')
This is illegal, as you have no symbol 'X' defined.
If you switch the order of your if check, you can pass legal ISBNs. However, it still fails on invalid codes with an X at the end.
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (eval(n[i])*(i+1))
if (checkSum%11 == 10 and n[9] == 'X') or \
checkSum%11 == eval(n[9]):
return True
else:
return False
Note also that you can short-cut that return logic by simply returning the expression value:
return (checkSum%11 == 10 and n[9] == 'X') or \
checkSum%11 == eval(n[9])
Eval is not the proper usage, nor is the way you use it correct. For example, see Wikipedia which shows the use. You probably want to use a try: except: pair.
try:
int(n[i]
except:
print "this character is not a digit"
A call to eval is sometimes used by inexperienced programmers for all
sorts of things. In most cases, there are alternatives which are more
flexible and do not require the speed penalty of parsing code.
For instance, eval is sometimes used for a simple mail merge facility,
as in this PHP example:
$name = 'John Doe';
$greeting = 'Hello';
$template = '"$greeting,
$name! How can I help you today?"';
print eval("return $template;");
Although this works, it can cause some security problems (see §
Security risks), and will be much slower than other possible
solutions. A faster and more secure solution would be changing the
last line to echo $template; and removing the single quotes from the
previous line, or using printf.
eval is also sometimes used in applications needing to evaluate math
expressions, such as spreadsheets. This is much easier than writing an
expression parser, but finding or writing one would often be a wiser
choice. Besides the fixable security risks, using the language's
evaluation features would most likely be slower, and wouldn't be as
customizable.
Perhaps the best use of eval is in bootstrapping a new language (as
with Lisp), and in tutoring programs for languages[clarification
needed] which allow users to run their own programs in a controlled
environment.
For the purpose of expression evaluation, the major advantage of eval
over expression parsers is that, in most programming environments
where eval is supported, the expression may be arbitrarily complex,
and may include calls to functions written by the user that could not
have possibly been known in advance by the parser's creator. This
capability allows you to effectively augment the eval() engine with a
library of functions that you can enhance as needed, without having to
continually maintain an expression parser. If, however, you do not
need this ultimate level of flexibility, expression parsers are far
more efficient and lightweight.
Thanks everyone. I don't know how I didn't think of using int(). The reason I used eval() was because the past few programs I wrote required something like
x = eval(input("Input your equation: "))
Anyways the function works now.
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (int(n[i])*(i+1))
if n[9] == 'X':
if checkSum%11 == 10: return True
else: return False
elif checkSum%11 == int(n[9]): return True
else: return False

Readable, controllable iterators?

I'm trying to craft an LL(1) parser for a deterministic context-free grammar. One of the things I'd like to be able to use, because it would enable much simpler, less greedy and more maintainable parsing of literal records like numbers, strings, comments and quotations is k tokens of lookahead, instead of just 1 token of lookahead.
Currently, my solution (which works but which I feel is suboptimal) is like (but not) the following:
for idx, tok in enumerate(toklist):
if tok == "blah":
do(stuff)
elif tok == "notblah":
try:
toklist[idx + 1]
except:
whatever()
else:
something(else)
(You can see my actual, much larger implementation at the link above.)
Sometimes, like if the parser finds the beginning of a string or block comment, it would be nice to "jump" the iterator's current counter, such that many indices in the iterator would be skipped.
This can in theory be done with (for example) idx += idx - toklist[idx+1:].index(COMMENT), however in practice, each time the loop repeats, the idx and obj are reinitialised with toklist.next(), overwriting any changes to the variables.
The obvious solution is a while True: or while i < len(toklist): ... i += 1, but there are a few glaring problems with those:
Using while on an iterator like a list is really C-like and really not Pythonic, besides the fact it's horrendously unreadable and unclear compared to an enumerate on the iterator. (Also, for while True:, which may sometimes be desirable, you have to deal with list index out of range.)
For each cycle of the while, there are two ways to get the current token:
using toklist[i] everywhere (ugly, when you could just iterate)
assigning toklist[i] to a shorter, more readable, less typo-vulnerable name each cycle. this has the disadvantage of hogging memory and being slow and inefficient.
Perhaps it can be argued that a while loop is what I should use, but I think while loops are for doing things until a condition is no longer true, and for loops are for iterating and looping finitely over an iterator, and a(n iterative LL) parser should clearly implement the latter.
Is there a clean, Pythonic, efficient way to control and change arbitrarily the iterator's current index?
This is not a dupe of this because all those answers use complicated, unreadable while loops, which is what I don't want.
Is there a clean, Pythonic, efficient way to control and change arbitrarily the iterator's current index?
No, there isn't. You could implement your own iterator type though; it wouldn't operate at the same speed (being implemented in Python), but it's doable. For example:
from collections.abc import Iterator
class SequenceIterator(Iterator):
def __init__(self, seq):
self.seq = seq
self.idx = 0
def __next__(self):
try:
ret = self.seq[self.idx]
except IndexError:
raise StopIteration
else:
self.idx += 1
return ret
def seek(self, offset):
self.idx += offset
To use it, you'd do something like:
# Created outside for loop so you have name to call seek on
myseqiter = SequenceIterator(myseq)
for x in myseqiter:
if test(x):
# do stuff with x
else:
# Seek somehow, e.g.
myseqiter.seek(1) # Skips the next value
Adding behaviors like providing the index as well as value is left as an exercise.

Doesn't accept the list index?

I have this peice of code:
n = int (input ('Enter the Number of Players: '))
m = [[j] for j in range (0, n)]
all_names= []
i = 0
while n > 1:
m[i] = input('Player {0}: '.format (i+1))
all_names.extend ([m[i]])
if m[i][0] != m[i-1][-1]:
b= m.pop (i)
n = n-1
if all_names.count (m[i]) == 2:
n = n-1
b= m.pop (i)
i = i+1
It says the index is out of range (second if clause)
but I dont get it, why?
I hate to not answer your question directly, but what you're trying to do seems... really confusing. Python has a sort of rule that there's supposed to be a really clear, clean way of doing things, so if a piece of code looks really funky (especially for such a simple function), it's probably not using the right approach.
If you just want to create a container of names, there are numerous simpler ways of doing it:
players=int(input("How many players?\n"))
player_names=set()
while len(player_names)<players:
player_names.add(input("What is player {}'s name?\n".format(len(player_names)+1)))
... will give you a set of unique player names, although this won't be ordered. That might matter (your implementation kept order, so maybe it is), and in this case you could still use a list and add a small check to make sure you were adding a new name and not repeatedly adding names:
players=int(input("How many players?\n"))
player_names=list()
while len(player_names)<players:
playname=input("What is player {}'s name?\n".format(len(player_names)+1))
if playname not in player_names:
player_names.append(playname)
I'm open to someone haranguing me about dodging the question, particularly if there's a purpose/reason for the approach the questioner took.
Length of m decreases every time the code enters the first if clause. However, you increment the value of i in each iteration. So, at the midpoint of length of m (if the 1st clause is entered always) or a little later, the value of i will be bigger than the value of m and you will get an index out of range.

Resources