Basically, I'm messing around in Python (3) and I'm trying to define some functions to do some common tasks in single lines, using as less memory as possible
For example, in video games, you might want a character to turn some degrees left/right, but not 0 degrees. So, given an integer x, how could you return a random integer between -x and +x` (inclusive) which isn't 0?
Note, I'm going for one-liners using minimum memory.
I'm posting an answer but I'd also like to know how other people would approach this.
EDIT: This isn' school homework or anything, I'm just designing a video game and defining a few functions which will come in handy. I said no 'If's because I was wondering if it was possible, and if so, how.
Thanks :-)
I would suggest:
import random
def randint_between(x):
return random.choice([-1, 1]) * random.randint(1, x)
I would use:
import random
def between(x):
return ((((random.randint(0, 1) * 2) - 1) * random.randint(1, x))
random.sample works with ranges which are very small compared to the range of numbers they provide.
This create a positive number between 1 and x and multiplies it with 1 or -1.
import random
def between(x):
return random.sample(range(1, x+1),k=1)[0] * (-2*random.randint(0,1)+1)
After looking at kch answer, I would probably go his approach.
Related
Okay, so I'm working on Euler Problem 12 (find the first triangular number with a number of factors over 500) and my code (in Python 3) is as follows:
factors = 0
y=1
def factornum(n):
x = 1
f = []
while x <= n:
if n%x == 0:
f.append(x)
x+=1
return len(f)
def triangle(n):
t = sum(list(range(1,n)))
return t
while factors<=500:
factors = factornum(triangle(y))
y+=1
print(y-1)
Basically, a function goes through all the numbers below the input number n, checks if they divide into n evenly, and if so add them to a list, then return the length in that list. Another generates a triangular number by summing all the numbers in a list from 1 to the input number and returning the sum. Then a while loop continues to generate a triangular number using an iterating variable y as the input for the triangle function, and then runs the factornum function on that and puts the result in the factors variable. The loop continues to run and the y variable continues to increment until the number of factors is over 500. The result is then printed.
However, when I run it, nothing happens - no errors, no output, it just keeps running and running. Now, I know my code isn't the most efficient, but I left it running for quite a bit and it still didn't produce a result, so it seems more likely to me that there's an error somewhere. I've been over it and over it and cannot seem to find an error.
I'd merely request that a full solution or a drastically improved one isn't given outright but pointers towards my error(s) or spots for improvement, as the reason I'm doing the Euler problems is to improve my coding. Thanks!
You have very inefficient algorithm.
If you ask for pointers rather than full solution, main pointers are:
There is a more efficient way to calculate next triangular number. There is an explicit formula in the wiki. Also if you generate sequence of all numbers it is just more efficient to add next n to the previous number. (Sidenote list in sum(list(range(1,n))) makes no sense to me at all. If you want to use this approach anyway, sum(xrange(1,n) will probably be much more efficient as it doesn't require materialization of the range)
There are much more efficient ways to factorize numbers
There is a more efficient way to calculate number of factors. And it is actually called after Euler: see Euler's totient function
Generally Euler project problems (as in many other programming competitions) are not supposed to be solvable by sheer brute force. You should come up with some formula and/or more efficient algorithm first.
As far as I can tell your code will work, but it will take a very long time to calculate the number of factors. For 150 factors, it takes on the order of 20 seconds to run, and that time will grow dramatically as you look for higher and higher number of factors.
One way to reduce the processing time is to reduce the number of calculations that you're performing. If you analyze your code, you're calculating n%1 every single time, which is an unnecessary calculation because you know every single integer will be divisible by itself and one. Are there any other ways you can reduce the number of calculations? Perhaps by remembering that if a number is divisible by 20, it is also divisible by 2, 4, 5, and 10?
I can be more specific, but you wanted a pointer in the right direction.
From the looks of it the code works fine, it`s just not the best approach. A simple way of optimizing is doing until the half the number, for example. Also, try thinking about how you could do this using prime factors, it might be another solution. Best of luck!
First you have to def a factor function:
from functools import reduce
def factors(n):
step = 2 if n % 2 else 1
return set(reduce(list.__add__,
([i, n//i] for i in range(1, int(pow(n,0.5) + 1)) if n % i
== 0)))
This will create a set and put all of factors of number n into it.
Second, use while loop until you get 500 factors:
a = 1
x = 1
while len(factors(a)) < 501:
x += 1
a += x
This loop will stop at len(factors(a)) = 500.
Simple print(a) and you will get your answer.
This is for an assignment at school and I'm stuck on this question.
Write an algorithm which displays on a new line on the screen, the first 20 values of multiples of 9, using a formula as follows.
9 * n, 9*(n+1),9*(n+2)
I am supposed to make an algorithm with pseudocode or a flow chart, I've been trying it with python3.4 but I cant seem to print out more than (9,18,27).
This is my code:
def multiples():
for n in range(0, 2):
a = 9*n, 9*(n+1), 9*(n+2)
print (a)
Can someone please tell me where I'm going wrong and possibly show me a better way of doing this with pseudocode or a flow chart. (Thats how the teacher wants it done) I just use Python to see if it actually works.
Thank you.
There you go
def multiples():
for n in range(0,20):
a = 9*n
print (a)
the n in for n in range(0,2) will vary, so no need to hardcode n+1, n+2
note also, if you're using python 2.7, you probably should use xrange instead of range (in this case it wont matter much, but for big loop it could)
Let me preface it with that I am new to python completely and programming for that matter and I need to create a program to print all Catalan numbers up to one trillion. I have the basics of the program written out but I can't seem to understand why I'm not getting the right numbers. Also I am running into programs when trying to graph the trend out. Thanks in advance and here is the code I have:
import numpy as np
import scipy as sp
from pylab import *
def Catalan(n):
if n==0:
return (1)
elif n==1:
return (1)
else:
return ((4*n+2)/(n+2))*Catalan(n-1)
for n in range(18):
print (Catalan(n))
n=np.linspace(0,18,100)
y=Catalan(n)
plot(n,y,'r')
show()
There are two main mistakes in your Catalan function.
First, as seen in http://en.wikipedia.org/wiki/Catalan_number, the formula you wrote is useful to compute Catalan(n+1) in terms of Catalan(n). To use it to compute Catalan(n) in terms of Catalan(n-1), as you do, you must shift the index. So it should be (4*n-2)/(n+1)*Catalan(n-1).
Second, python uses integer arithmetic when dealing with ints. So, the quotients get rounded down to ints (this is, 5/2 gives 2, not 2.5). One way to deal with this is to write first the product and then the quotient: (4*n-2)*Catalan(n-1)/(n+1).
Nevermind programming, your formula looks broken. Take n=2:
return ((4*n+2)/(n+2))*Catalan(n-1)
= ((4*2+2)/(2+2))*Catalan(2-1)
= (( 8 +2)/( 4 ))*Catalan( 1 )
= (( 10 )/ 4 )* 1
= 2.5
I want to create a program that will take a positive integer n, then create a list consisting of n random numbers between 0 and 9. Once I find that, I want to return the average of those numbers. Am I on the right track?
import random
def randomNumbers(n):
n > 0
myList = random.randrange(0,9)
I know I haven't gotten to the second part yet.. but is this how I start out?
This is my final code:
import random
def randomNumbers(n):
myList = []
for i in range(n):
myList.append(random.randrange(0, 9))
return sum(myList)/len(myList)
Could someone direct me to a page where it talks about a for and while loops and when to use them?
The equals sign (=) is only used to set the value of the "entire variable" (like myList). You want a list, which will then have values added to it, so start with this:
myList = []
to make it an empty list.
Now you had the right idea about repeating something while n > 0- but the ideal way is to use a for loop:
for i in range(n): # repeats the following line(s) of code n times
myList.append(random.randrange(0,9))
To find the average, take the sum and divide by the number of items (n).
If you are using Python 2.x, then note that an integer divided by another integer will round to an integer, so you should convert into a float (decimal) before division:
average = sum(myList)*1.0/n
For Python 3.x, int/int gives a float like you might expect:
average = sum(myList)/n
To return a value, use the return statement:
return average
It helps to break down what you're trying to do, if it's too complicated in a single explanation.
Take a positive integer, n
create a list of n length
each element should be a number 0-9
return the average of that list.
Let's tackle this one at a time:
take a positive integer, n
def randomNumbers(n):
if type(n)==int and n>0:
when you say def randomNumbers(n): you're saying
Hey, computer, this is a function called "randomNumbers",
and it's going to be called with a single thing in parentheses
and we're going to call that `n` from now on.
Here's what that function does:
But you haven't ensured that n is an integer greater than 0. It's just a thing. So we need to ensure that it is. if type(n)==int and n>0: does that by checking that the type of n is int and that n's value is positive (greater than 0).
That's the first bullet point done. Next is creating a list of n length with each element being an integer numbered 0-9. These should be random[1].
result=list()
for i in range(n):
result.append(random.randint(0,9))
This is less complicated than it seems. You start by creating a list that you'll call result (or whatever you want, I don't care). The for loop is just saying:
Hey, computer, you're gonna do the same thing a number of times,
and that number is "until you've counted from 0 to n times".
And when you do that, "i" is going to represent where you are
in that "0 to n" range. Here's what you're gonna do all those times:
So what are you going to do n times? You're going to append a random integer between 0 and 9 to that list we just called result. That's what result.append(random.randint(0,9)) does.
So next you want to return the average of that list. Well, the average of a list is the sum of the numbers in the list divided by the length.
return sum(result)/float(len(result))
sum and len can take any iterable, like a list, or a set, or whatever you please, and give you the sum, length, or whatever else you ask for. These are great tools to learn about. So do it. Elsewhere, preferably, frankly.
Now, if you've been really attentive, you'll see that so far we have this:
import random # I added this secretly, sorry!
def randomNumbers(n):
if type(n)==int and n>0:
result=list()
for i in range(n):
result.append(random.randint(0,9))
return sum(result)/float(len(result))
Which is great! That's what you need; try print(randomNumbers(5)) and you'll get exactly what you're looking for: the average of a bunch of random numbers.
But what if you try print(randomNumbers('foo'))? You probably get None, right? That's because it didn't fit in with the if statement we wrote, and so it never went down that path! You need to write an else statement so things that aren't positive integers still get some love.
else:
return "That's no good. Try a positive integer instead"
Stupid, but it works. Someday you'll learn about raising exceptions and all that smooth Jazz, but until then, it's fine just to say
Hey, computer, if n wasn't up to my stratospheric expectations,
then just spit back this message. I'll understand.
So at the end of the day you've got
import random
def randomNumbers(n):
if type(n)==int and n>0:
result=list()
for i in range(n):
result.append(random.randint(0,9))
return sum(result)/float(len(result))
else:
return "That's no good. Try a positive integer instead"
There's some stuff you can do with this code to make it more efficient. To make you curious, I'll give an example:
from random import randint
def randy(n:int):
return sum({randint(0,9) for i in range(n)})/n if n>0 else "Try again."
That gets into list comprehensions, sets, function annotations, and other stuff that you don't need to worry about. And frankly, that line of code might be too convoluted for good "Pythonic" code, but it's possible, which is cool.
Keep at it, and don't get discouraged. Python makes an effort to explain what you did wrong, and the community is generally pretty good if you've done your due diligence. Just don't abuse them(/us) by asking for help without seeking answers on your own first.
[1]technically they're pseudorandom but true randomness is hard to produce.
You are not on the right track. See this variate for the track.
import random
def randomNumbers(n):
l = random.sample(xrange(10), n)
return sum(l)/len(l)
Could be done more concisely with a list comprehension:
myList = [randrange(0, 9) for x in xrange(n)]
average = sum(myList) / len(myList)
You should think about making use of Numpy. This would make your task easy.
import numpy as np
def myfunc(n):
x = np.random.randint(low=0, high=10, size=n)
return np.mean(x)
I have a ridiculous code segment in one of my programs right now:
str(len(str(len(var_text)**255)))
Is there an easy way to shorten that? 'Cause, frankly, that's ridiculous.
A option to convert a number >500 digits to scientific notation would also be helpful
(that's what I'm trying to do)
Full code:
print("Useless code rating:" , str(len(var_text)**255)[1] + "e" + str(len(str(len(var_text)**255))))
TL;DR: y = 2.408 * len(var_text)
Lets assume that your passkey is a string of characters with 256 characters available (0-255). Then just as a 16bit number holds 65536 numbers (2**16) the permutations of a string of equal length would be
n_perms = 256**len(passkey)
If you want the number of (decimal) digits in n_perms, consider the logarithm:
>>> from math import log10
>>> log10(1000)
3.0
>>> log10(9999)
3.9999565683801923
>>>
So we have length = floor(log10(n_perms)) + 1. In python, int rounds down anyway, so I'd say you want
n_perms = 256**len(var_text)
length = int(log10(n_perms)) + 1
I'd argue that 'shortening' ugly code isn't always the best way - you want it to be clear what you're doing.
Edit: On further consideration I realised that choosing base-10 to find the length of your permutations is really arbitrary anyway - so why not choose base-256!
length = log256(256**len(var_text)
length = len(var_text) # the log and exp cancel!
You are effectively just finding the length of your passkey in a different base...
Edit 2: Stand back, I'm going to attempt Mathematics!
if x = len(var_text), we want y such that
y = log10(256**x)
10**y = 256**x
10**y = (10**log10(256))**x
10**y = (10**(log10(256)x))
y = log10(256) * x
So, how's this for short:
length = log10(256) * len(var_text) # or about (2.408 * x)
This looks like it's producing a string version of the number of digits in the 255th power of the length of a string. Is that right? I'd be curious what that's used for.
You could compute the number differently, but it's not shorter and I'm not sure it's prettier:
str(int(math.ceil(math.log10(len(var_text))*255)))
or:
"%d" % math.ceil(math.log10(len(v))*255)
Are you trying to determine the number of possible strings having the same length as var_text? If so, you have your base and exponent reversed. You want to use 255**len(var_text) instead of len(var_text)**255.
But, I have to ask ... how long do these passkeys get to be, and what are you using them for?
And, why not just use the length of the passkey as an indicator of its length?
Firstly, if your main problem is manipulating huge floating point expressions, use the bigfloat package:
>>> import bigfloat
>>> bigfloat.BigFloat('1e1000')
BigFloat.exact('1.0000000000000001e+1000', precision=53)
As for the details in your question: len(str(num)) is approximately equal to log(num, 10) + 1. This is not significantly shorter, but it's certainly a better way of expressing it in code (for the benefit of anyone who doesn't know that off the top of their head). You can then simplify it with log laws:
len(str(x**y))
= log(x**y, 10) + 1
= y * log(x, 10) + 1
So maybe you'll find:
"%i" % (log(len(var_text),10)*255 + 1)
... is better? It's not significantly shorter, but it's a much clearer mathematical relationship between input and output.