I am trying to run a random variable multiple times - python

I am very new to python, and am running into an issue I don't fully understand. I am trying to get a random variable to run multiple times, but for some reason it just returns the same random value x times.
I am not entirely certain what to try aside from the code I have already done.
lowTreasureList = "50 gold", "Healing Potion", "10x Magic Arrows", "+1 Magic Weapon"
def ranLowLoot(lowLootGiven):
# This function returns a random string from the passed list of strings.
lootIndex = random.randint(0, len(lowLootGiven) - 1)
return lowLootGiven[lootIndex]
lowLoot = ranLowLoot(lowTreasureList)
treasureSelection = int(input())
if treasureSelection == 1:
numLowTreasure = int(input('How many treasures? '))
for i in range(numLowTreasure):
ranLowLoot(lowTreasureList)
print(lowLoot)
When I do this I get the same random treasure (numLowTreasure) times, but I am trying to get it to select a new random treasure each time.

If you haven't already, it will help to read the documentation on the random module.
There are three alternatives to random.randint that are more suited to your purpose:
random.randrange(start, stop, [step]): step is optional and defaults to one. This will save you the len(...) - 1 you are using to get lootIndex, since stop is an exclusive bound.
random.randrange(stop): uses a default start of zero and default step of 1, which will save you passing 0 as your start index.
random.choice(seq): you can pass your function's parameter lowLootGiven to this as seq, which will save you from using indices and writing your own function entirely.
As for why you're getting the repeated treasure, that's because you aren't updating your variable lowLoot in your for loop. You should write:
for i in range(numLowTreasure):
lowLoot = ranLowLoot(lowTreasureList)
print(lowLoot)
Last thing I want to say is that python is nice for writing simple things quickly. Even if there was some bigger context that you were writing this code in, I might have written it like this:
lowTreasureList = ("50 gold", "Healing Potion", "10x Magic Arrows", "+1 Magic Weapon")
if int(input()) == 1:
for i in range(int(input('How many treasures? '))):
print(random.choice(lowTreasureList))
Using the round parentheses around the tuple declaration like I did isn't necessary in this case, but I like to use it because if you want to make the tuple declaration span multiple lines, it won't work without them.
Reading documentation on standard libraries is something I almost always find helpful. I think Python's documentation is great, and if it's bit too much to digest early on, I found tutorialspoint to be a good place to start.

The problem is that in the main loop you are discarding the result of the call to ranLowLoot(). As a minimal fix, in the main loop assign the result of that function call. Use:
lowLoot = ranLowLoot(lowTreasureList)
rather than simply:
ranLowLoot(lowTreasureList)
As a better fix, ditch your function completely and just use random.choice() (which does what you are trying to do, with much less fuss):
import random
lowTreasureList = ["50 gold", "Healing Potion", "10x Magic Arrows", "+1 Magic Weapon"]
treasureSelection = int(input())
if treasureSelection == 1:
numLowTreasure = int(input('How many treasures? '))
for i in range(numLowTreasure):
lowLoot = random.choice(lowTreasureList)
print(lowLoot)

Related

Having some problems with Project Euler 31

I've been working on solving Problem 31 from Project Euler. I'm almost finished, but my code is giving me what I know is the wrong answer. My dad showed the right answer, and mine is giving me something completely wrong.
coins = (1,2,5,10,20,50,100,200)
amount = 200
def ways(target, avc):
target = amount
if avc <= 1:
return 1
res = 0
while target >= 0:
res = res + ways(target, avc-1)
target = target - coins[avc]
return res
print(ways(amount, 7))
This is the answer I get.
284130
The answer is supposed to 73682.
EDIT: Thank you to everyone who answered my question. Thanks to all of you, I have figured it out!
I see several ways how you can improve the way you're working on problems:
Copy the task description into your source code. This will make you more independent from Internet resources. That allows you to work offline. Even if the page is down or disappears completely, it will enable someone else understanding what problem you're trying to solve.
Use an IDE that does proper syntax highlighting and gives you hints what might be wrong with your code. The IDE will tell you what #aronquemarr mentioned in the comments:
There's also a thing called magic numbers. Many of the numbers in your code can be explained, but why do you start with an avc of 7?. Where does that number come from? 7 does not occur in the problem description.
Use better naming, so you understand better what your code does. What does avc stand for? If you think it's available_coins, that's not correct. There are 8 different coins, not 7.
If it still does not work, reduce the problem to a simpler one. Start with the most simple one you can think of, e.g. make only 1 type of coin available and set the amount to 2 cent. There should only be 1 solution.
To make sure this simple result will never break, introduce an assertion or unit test. They will help you when you change the code to work with larger datasets. They will also change the way you write code. In your case you'll find that accessing the variable coins from an outer scope is probably not a good idea, because it will break your assertion when you switch to the next level. The change that is needed will make your methods more self-contained and robust.
Then, increase the difficulty by having 2 different coins etc.
Examples for the first assertions that I used on this problem:
# Only 1 way of making 1 with only a 1 coin of value 1
assert(find_combinations([1], 1) == 1)
# Still only 1 way of making 1 with two coins of values 1 and 2
assert(find_combinations([1,2], 1) == 1)
# Two ways of making 2 with two coins of values 1 and 2
assert(find_combinations([1,2], 2) == 2)
As soon as the result does no longer match your expectation, use the debugger and step through your code. After every line, check the values in the debugger against the values that you think they should be.
One time, the debugger will be your best friend. And you can never imagine how you did stuff without it. You just need to learn how to use it.
Firstly, read Thomas Weller's answer. They give some excellent suggestions as to how to improve your coding and problem solving.
Secondly, your code works, and gives the correct answer after the following changes:
As suggested, remove the target = amount line. You're resigning the argument of the function to global amount on each call (even on the recursive calls). Having removed that the answer comes up to 3275 - still not the right answer.
The other thing you have to remember is that Python is 0-indexed. Therefore, your simplest case condition ought to read if avc <= 0: ... (not <=1).
Having made these two changes, your code gives the correct answer. Here is your code with these changes:
coins = (1,2,5,10,20,50,100,200)
amount = 200
def ways(target, avc):
if avc <= 0:
return 1
res = 0
while target >= 0:
res = res + ways(target, avc-1)
target = target - coins[avc]
return res
print(ways(amount, 7))
Lastly, there are plenty of Project Euler answers out there. Having solved the yourself, it might be useful to have a look at what others did. For reference, I have not actually solved this Project Euler before, so I had to do that first. Here is my solution. I've added a pile of comments on top of it to explain what it does.
EDIT
I've just noticed something quite worrying: Your code (after fixes) works only if the first element of coins is 1. All the other elements can be shuffled:
# This still works ok
coins = (1,2,200,10,20,50,100,5)
# But this does not
coins = (2,1,5,10,20,50,100,200)
To ensure that this is always the case, you can just do the following:
coins = ... # <- Some not necessarily sorted tuple
coins = tuple(sorted(coins))
In principle there are a few other issues. Your solution breaks for non-unique values coins, and which don't include 1. The former you could fix with the use of sets, and the latter by modifying your if avc <= 0: case (check for the divisibility of the target by the remaining coin). Here is you piece of code with these changes implemented. I've also renamed the variables and the function to be a bit easier to read, and used coins as the argument, rather that avc pointer (which, by the way, I could not stop reading as avec):
unique = lambda x: sorted(set(x)) # Sorted unique list
def find_combinations(target, coins):
''' Find the number of ways coins can make up the target amount '''
coins = unique(coins)
if len(coins)==1: # Only one coin, therefore just check divisibility
return 1 if not target%coins[0] else 0
combinations = 0
while target >= 0:
combinations += find_combinations(target, coins[:-1])
target -= coins[-1]
return combinations
coins = [2,1,5,10,20,50,100,200] # <- Now works
amount = 200
print(find_combinations(amount, coins))

How do I execute functions via sys.argv

I'm very new to coding (started 2 days ago) and for practice a friend gave me the task of writing a program that provides either the nth Fibonacci number or the sequence up to the nth point. I successfully completed that task using input() and directly asking the user for an n, now he extended the task and asked me to try getting the same results using sys.argv
After extensive use of google I figured out how to print all the given arguments and count them, but I cannot figure out any way of using those arguments in a function. Unfortunately I also can't seem to find the right keywords for google, leaving me a little stuck in no mans land.
Here's my most recent attempt:
import sys
from math import sqrt
print('Number of arguments:', len(sys.argv), 'arguments.')
print ('Argument List:', str(sys.argv))
Fibonacci = sys.argv[0]
value = sys.argv[1]
sequence = sys.argv[2]
def fib(value): int(1/sqrt(5)*(((1+sqrt(5))/2)**value-(((1-sqrt(5))/2)**value)))
print("The {}. value is".format(value), fib(value))
input("Close")
(Small detail, albeit unimportant: I translated the strings from German to English, which is also why it says "{}. value" rather than "{}st/nd/rd/th", the differentiation between those cases is a problem for a later date.)
Now I expect to be miles off target here, but using some of the expressions that worked for my input() based code is pretty much the last idea I have right now. Can anyone give me a pointer on how I can proceed here? Even a hint on what I should google would help, but as of now I'm completely out of ideas.
EDIT: I don't know if this is what you're supposed to do, but I've solved my problem and I thought I might as well post my code in case someone else stumbles upon this thread with a similar question. Here's my solution.
import sys
from math import sqrt
Fibonacci = sys.argv[0]
Entscheidung = (sys.argv[1])
n = int(sys.argv[2])
sequence = []
if Entscheidung == "Stelle":
def fib(n): return int(1/sqrt(5)*(((1+sqrt(5))/2)**n-((1-sqrt(5))/2)**n))
print("Die {}. Stelle beträgt:{}".format(n, fib(n)))
elif Entscheidung == "Folge":
def fib(n): return int(1/sqrt(5)*(((1+sqrt(5))/2)**n-((1-sqrt(5))/2)**n))
for i in range(n):
sequence.append(fib(i + 1))
print('[%s]' % ', '.join(map(str, sequence)))
input("Schließen")
Note that I'm still an absolute beginner and this solution might be inefficient, badly written, confusingly formatted, I wouldn't know. All I can guarantee is that it does the job.
Yup, n = int(sys.argv[2]) was the charm.
Now that you're an expert at cracking sys.argv, you might want to $ pip install click and let that package do some of the parsing for you: https://click.palletsprojects.com/en/7.x/
Conditionally executing def is an option, I suppose, but a bit odd.
Pasting the same definition into both if branches is not helpful.
Just def it once, up top.
Here's a refactoring of your code with the following changes:
Don't define the same function twice. Instead, define two separate functions, and decide which one to call depending on the desired semantics.
Don't capitalize variables. Capitalized names are conventionally reserved for class names in Python.
Remove the final input. Surely your friend wanted you to make a program which can be properly reused; requiring user interaction ruins that.
Don't use sys.argv[0] for anything. If it's not used, there's no need to capture it (and if you should need it later, it's still there).
Wrap the entry point in if __name__... so that this piece of code can be imported into another program without side effects.
from math import sqrt
def fib_nth(n):
return int(1/sqrt(5)*(((1+sqrt(5))/2)**n-((1-sqrt(5))/2)**n))
def fib_seq(n):
sequence = []
for i in range(n):
sequence.append(fib_nth(i + 1))
return sequence
if __name__ == '__main__':
import sys
entscheidung = sys.argv[1]
n = int(sys.argv[2])
if entscheidung == "Stelle":
print("Die {}. Stelle beträgt:{}".format(n, fib_nth(n)))
elif entscheidung == "Folge":
print('[%s]' % ', '.join(map(str, fib_seq(n))))

Repeating a function with another function

for an assignment we needed to make a function that flipped a coin and another to flip it 100 times. I was able to make a function that flipped a coin, but got stuck when trying to call it a 100 times with another function. This is what I have right now:
import random
def TC():
face = random.randint(0,1)
if face == 1:
return "head"
else:
return "tail"
print TC()
def ply(flips):
for i in range(flips):
return TC()
print ply(100)
When I run it it just says 'none.' Please tell me where I am going wrong. Thank You!
Just to start, your method naming is very bad. I doubt this is how your professor is teaching you to name methods and variables. It's ugly, against Python standards and hard to read I suggest you take some time and read PEP 8 it's how python was intended to be written.
So instead of TC you should use something like flip_coin and instead of ply use something like play_coin_flip or even simply play.
Next I don't know if I'm stepping outside of what you have learned but instead of using randon.randint you can use randon.choice.
And finally, as others have said, when you return you quit any other execution in a function and return whatever variable you retrun in that statement thus nullifying any other iterations of the loop you're performing. I suggest something like the below as a better program with corrections applied to make it function as intended.
from random import choice
faces = ['head', 'tail']
def flip_coin():
face = choice(faces)
return face
def play_coin_flip(flips = 1):
for i in range(flips):
print(flip_coin)
if __name__ == "__main__":
play_coin_flip(100)

What is the right balance for logging, commenting and test cases when coding?

One thing I have noticed about when I code is that commenting, logging and writing test cases takes me out of any flow I am in and slows down my coding speed. So I am wondering if I do it too much/more than necessary. If I am using a debug logger, is there such thing as logging TOO much stuff? Also, is there a way to make the output log file color coordinated so that I can easily find issues/places where the value is not what I expect? Also, should I comment everything or just certain places explaining why I do something? Finally, what should I be making test cases for? Is it every single if statement or just every function?
Take this code for example:
def parse_data(raw_):
# Assume raw is a dict
parsed_output = dict()
for i in raw_.keys():
if i["type"] == "child":
parsed_output[i] = {"type": "Student"}
else:
parsed_output[i] = {"type": "Teacher"}
if 14 <= i["age"] <= 19:
parsed_output[i]["division"] = "High School"
elif 11 <= i["age"] < 14:
parsed_output[i]["division"] = "Middle School"
elif i["age"] < 11:
parsed_output[i]["division"] = "Elementary"
else:
parsed_output[i]["division"] = "Staff"
parsed_output[i]["name"] = i["name"]
return parsed_output
Should I add in a logging line after every if saying that it found a Student in High School? Or should I just put a debug line saying the # of People found in raw, and one after that says the number of people found in High School, Middle School, Elementary, and staff? For test cases, should I write one for each if statement in this function, or for the function as a whole? Also, for a function like this, do you have separate files you use as "test data" and expected output? Finally, in this function, should I comment before each of the if blocks what each is looking for, or only comment before the first parsed_output to explain that the reason I created a dict at parsed_output[i] was to avoid an index error?

Python: How to test efficiency of IF statement to a Dictionary Case statement?

I'd like to be able to test the efficiency of an IF statement to a dictionary case statement hack in Python. Since there is no case statement I am currently using the dictionary method. It looks like so....
self.options = {"0": self.racerOne,
"1": self.racerTwo,
"2": self.racerThree,
"3": self.racerFour,
"0f": self.racerFinish,
"1f": self.racerFinish,
"2f": self.racerFinish,
"3f": self.racerFinish,
"x": self.emptyLine,
"t": self.raceTime,
"G": self.letsGo,
"CD": self.countDown,
"C": self.emptyLine,
}
Real world data will vary but I have a way to run a controlled test and that reads 688 lines of streaming data over 6.4 seconds.
I have also read this post: Python Dictionary vs If Statement Speed I am going to check out the cProfile method as well.
Does anyone have any other suggestions on how I can accurately measure an IF statement compared to the dictionary option? By efficient I guess that means using the least processing power and can keep up with the stream better.
Over those 6.4 seconds I read each line of streaming data, parse it, evaluate it, then visually display it in real time. I don't think there is going to be much different in running my application on a Win or OSX system but it also have to run on a Raspberry Pi where processing power is limited.
Thanks in advance.
It sounds as though your major areas to optimize will not be this statement.
However, out of curiosity, I examined it anyway. The intuitive answer, which is given in the question you linked to, is that python dictionaries are implemented as hash tables. Lookup should scale at around O(1) with the number of items. If statements along what you showed will scale at O(n), as each will be done sequentially. Running 1000 random numbers through functions using each, with anywhere from 2 to 1000 choices, I got the following timing (y scale is in seconds per choice, and is log scale). If chains are blue, dict lookup is green. The x scale is in number of possible choices:
As can be seen, lookup is fast, and much faster than long if statement chains.
Even for short chains, in this code, lookup is still faster or around the same:
But note the times here. We're talking about times in the sub-microsecond range per choice on my computer: around 600ns for a low number of choices. At that point, the overhead may be from things as simple as function calls. On the other hand, if you have a huge number of possible choices, the best thing to use should be pretty clear.
The code for the above is below. It uses numpy to keep track of all the times. For simple timing issues like this, it's usually easiest to just use time.time() to get a time value before and after whatever you want to do. For something very fast, you'll need to loop through multiple times and take an average of the times.
I should add the caveat that the way I created the if statement chains was mildly evil. It's possible that in doing so (with an exec statement) the function was somehow not optimized in the same way: I'm not an expert on python internals.
import numpy as np
import time
def createdictfun(n):
optdict = { x: 2*x for x in range(0,n) }
def dictfun(x):
return optdict[x]
return dictfun
def createiffun(n):
s="def iffun(x):\n"
s+=" if x==0: return 0\n"
for i in range(1,n):
s+=" elif x=={}: return {}\n".format(i,i*2)
s+=" else: raise ValueError\n"
exec s
return iffun
ntry=10
maxchoice=1000
trialsize=1000
ifvals=np.zeros((maxchoice,2))
dictvals=np.zeros((maxchoice,2))
ns=np.arange(1,maxchoice)
for n in ns:
ift = np.zeros(ntry)
dictt = np.zeros(ntry)
vals=np.random.randint(0,n,size=trialsize)
iffun = createiffun(n)
dictfun = createdictfun(n)
for trial in range(0,ntry):
ts=time.time()
for x in vals:
iffun(x)
ift[trial]=time.time()-ts
ts=time.time()
for x in vals:
dictfun(x)
dictt[trial]=time.time()-ts
ifvals[n,0]=np.mean(ift)/trialsize
ifvals[n,1]=np.std(ift)/trialsize
dictvals[n,0]=np.mean(dictt)/trialsize
dictvals[n,1]=np.std(dictt)/trialsize
print str(n)+" ",

Categories

Resources