itertools combinations in tandem with looping - python

I have the following Python code. Because random is being used, it generates a new answer every time:
import random
import numpy as np
N = 64 # Given
T = 5 # Given
FinalLengths = []
for i in range(T):
c = range(1, N)
x = random.sample(c, 2) # Choose 2 random numbers between 1 and N-1
LrgstNode = max(x)
SmlstNode = min(x)
RopeLengths = [SmlstNode, LrgstNode - SmlstNode, N - LrgstNode]
S = max(RopeLengths)
N = S
FinalLengths.append(S)
avgS = np.mean(FinalLengths) # Find average
print("The mean of S is {}".format(avgS))
My research has led me to possibly using itertools combinations in order to produce all possible combinations within the range and get the avg to converge. If so, how?
Thank you.

It sounds like you're after something like this:
import random
import numpy as np
from itertools import combinations
N = 64 # Given
T = 5 # Given
FinalLengths = []
for i in range(T):
c = list(range(1, N))
for x in combinations(c, 2):
S = max([min(x), max(x) - min(x), N - max(x)])
N = S
FinalLengths.append(S)
avgS = np.mean(FinalLengths) # Find average
print("The mean of S is {}".format(avgS))
To use combinations(l, size) we can pass in a list l and the size of each combination tuple, and int size. That's all there is to it!

Related

How to generate k percentages? [duplicate]

This question already has answers here:
Getting N random numbers whose sum is M
(9 answers)
Closed 8 months ago.
Given k a random integer between 2 and 7. How to generate a list of k positive numbers whose sum is equal to 1?
Examples of possible expected results:
k = 3 -> list = [0.23, 0.57, 0.2]
k = 3 -> list = [0.41, 0.44, 0.15]
K = 2 -> list = [0.95, 0.5]
You can generate k random numbers (for example in the range 0-1), then divide by their sum.
Example with numpy (for efficiency):
k = 3
import numpy as np
a = np.random.random(k)
out = (a/a.sum()).tolist()
pure python:
k = 3
import random
l = [random.random() for _ in range(k)]
s = sum(l)
out = [e/s for e in l]
example: [0.27830153962545046, 0.19826407925979248, 0.523434381114757]
I hope you are well.
you can use this code to do this:
import numpy as np
def softmax(x):
res = np.exp(x - np.max(x))
return res / res.sum()
size=5
list=np.random.random(size)
list=softmax(list)
Using a infinite loop which break once get all k numbers. Repeated numbers may occurs.
import random
#random.seed(3) # for testing purposes
k = 5
partition_of_unit = []
while True:
if len(partition_of_unit) == k-1:
break
n = random.random()
if sum(partition_of_unit) + n < 1:
partition_of_unit.append(n)
partition_of_unit.append(1-sum(partition_of_unit))
print(partition_of_unit)
#[0.6229016948897019, 0.029005228283614737, 0.11320596465314436, 0.013114189588902203, 0.22177292258463677]
print(sum(partition_of_unit))
#1.0

How do I run this function for multiple values of N?

I am trying to run the code below for N = np.linspace(20,250,47), but I get multiple errors when trying to change the N. I am new to python and am not sure how to get multiple values of this function using multiple values of N. Below is the code with N = 400 and it does work, but I am not sure how to make it work for multiple N's at the same time.
import matplotlib.pyplot as plt
import numpy as np
S0 = 9
K = 10
T = 3
r = 0.06
sigma = 0.3
N = 400
dt = T / N
u = exp(sigma*sqrt(dt)+(r-0.5*sigma**2)*dt)
d = exp(-sigma*sqrt(dt)+(r-0.5*sigma**2)*dt)
p = 0.5
def binomial_tree_put(N, T, S0, sigma, r, K, array_out=False):
dt = T / N
u = exp(sigma*sqrt(dt)+(r-0.5*sigma**2)*dt)
d = exp(-sigma*sqrt(dt)+(r-0.5*sigma**2)*dt)
p = 0.5
price_tree = np.zeros([N+1,N+1])
for i in range(N+1):
for j in range(i+1):
price_tree[j,i] = S0*(d**j)*(u**(i-j))
option = np.zeros([N+1,N+1])
option[:,N] = np.maximum(np.zeros(N+1), K - price_tree[:,N])
for i in np.arange(N-1, -1, -1):
for j in np.arange(0, i+1):
option[j, i] = np.exp(-r*dt)*(p*option[j, i+1]+(1-p)*option[j+1, i+1])
if array_out:
return [option[0,0], price_tree, option]
else:
return option[0,0]
Suppose you have a list of values for N e.g N = [400, 300, 500, 800], then you need to call the function for every value, you can use a loop for that.
For example,
for num in N:
binomial_tree_put(num, *other arguments*)
np.linspace() creates an np.array but the function expects a sinlge integer. If you want to execute a function for each element contained inside a array/list, you can do that inside a loop like this:
# your code as defined above goes here
for num in np.linspace(20,250,47):
N = int(num) # you could just put N in the line above - this is just to illustrate
binomial_tree_put(N, T, S0, sigma, r, K, array_out=False)
Be aware, depending on how long your function takes to execute and how many elements are in your iterable (e.g. 47 for your case), it may take a while to execute.
Edit: I also noticed you seem to be missing an import in your example code. exp() and sqrt() are part of the math module.
You can also use partial function, like this:
from functools import partial
N = [1, 2, ...] # all your N values
binom_fct = partial(binomial_tree_put, T=T, S0=S0, sigma=sigma, r=r, K=K, array_out=array_out)
for num in N:
binom_fct(num)
partial help here

How to minimize code when there are lot of lists?

I'm making a code to simulate a Brownian motion.
from random import random
import matplotlib.pyplot as plt
import numpy as np
N=100
p=0.5
l=1
x1=[]
x2=[]
x1.append(0)
x2.append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = x1[i-l] + step
x1.append(X1)
for i in range(1, N):
step = -l if random() < p else l
X2 = x2[i-l] + step
x2.append(X2)
x1mean=np.array(x1)
x2mean=np.array(x2)
mean=[]
for j in range (0,N):
mean.append((x1mean[j]+x2mean[j])/2.0)
plt.plot(mean)
plt.plot(x1)
plt.plot(x2)
plt.show()
This code makes the displacement for 2 diferent particles, but in order to calculate the mean displacement properly, I would need to have a great number of particles, likes 100. As you can see, I'm looking for a way to condensate the code because I cannot repetat the same code 100 times.
Is there a way to create a loop that makes all this code in function of 1 variable, i.e. the number of particles?
Thanks.
I can't provide you a working python code, because until now I did not write a single line of python code. But I can give you an idea how to solve your problem.
Assumptions:
N : Number of Moves
P : Number of Particles
Step 1:
Create a method generating your array/list and returning it. So you can re-use it and avoid copying your code.
def createParticleMotion(N, p, l):
x1=[]
x1.append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = x1[i-l] + step
x1.append(X1)
return x1
Step 2:
Create a list of lists, lets call it particleMotions. The list it selves has P list of your N moves. Fill the list within a for loop for you number of particles P by calling the method from the first step and append the list paticleMotions by the returned list/array.
May be the answer for Python: list of lists will help you creating this.
Step 3:
After you created and filled particleMotions use this list within a double for loop and calculate the mean and store it in a list of means.
mean=[]
for n in range (0,N):
sum=0
for p in range (0,P):
sum = sum + particleMotions[p][n]
mean.append(sum/P)
And now you can use a next for loop to plot your result.
for particle in range (0,P):
plt.plot(particleMotions[particle])
So again don't blame me for syntax errors. I am no phyton developer. I just want to give you a way to solve your problem.
This?
from random import random
import matplotlib.pyplot as plt
import numpy as np
N=100
p=0.5
l=1
mydict = {}
for n in range(100):
mydict[n] = []
mydict[n].append(0)
for i in range(1, N):
step = -l if random() < p else l
X1 = mydict[n][i-l] + step
mydict[n].append(X1)
for k,v in mydict.iteritems():
plt.plot(v)
# mean
plt.plot([np.mean(i) for i in mydict.values()])
plt.show()

How do I implement restricted combinations iterator?

I want something equivalent to the following code.
The following code generate poker possible hand patterns.
from itertools import combinations
m = 13
n = 4
x = range(m) * n
y = 5
pattern = set()
for i in combinations(x, y):
pattern.add(tuple(sorted(i)))
I tried to use itertools.combinations_with_replacement. Not surprisingly there are combinations like (0, 0, 0, 0, 0) or (1, 1, 1, 1, 1). But there are no 5 Queens and 5 Kings in a cards. So I don't want to take 5 same things. How do I implement restricted combinations iterator.
from itertools import combinations_with_replacement
m = 13
n = 4
x = range(m)
y = 5
pattern = []
for i in combinations_with_replacement(x, y):
pattern.append(i)
I want like the following code.
Pseudo code:
m = 13
n = 4
x = range(m)
y = 5
pattern = []
for i in combinations_with_replacement_restricted(x, y, max=n):
pattern.append(i)
P.S. Because I'm English learner, please modify my grammar mistakes.
After reading the docs: https://docs.python.org/2/library/itertools.html?highlight=combinations#itertools.combinations_with_replacement
I found that a built-in solution for your target don't exists.
So if you want to make this, I think the two solution may be suitable:
[1]. Make all your cards distinct:
from itertools import combinations
m = 13
n = 4
x = range(m * n)
y = 5
pattern = set()
# combinations from range(0, 52)
for i in combinations(x, y):
# so p/n maps to range(0, 13)
pattern.add((p / n for p in sorted(i)))
[2]. Exclude all invalid results:
from itertools import combinations
m = 13
n = 4
x = range(m) * n
y = 5
pattern = set()
for i in combinations(x, y):
if min(i) == max(i):
continue
pattern.add(tuple(sorted(i)))
You could do this :
import itertools
# each card is a pair (value, color)
cards = [ (value, color) for value in xrange(13) for color in xrange(4) ]
# all 5 cards combinations within 52 cards
for hand in itertools.combinations( cards, 5 ) :
print hand

Random int without importing 'random'

is there a way to let the program select a random number between say 1 and 1,000 without importing random?
Based on random source code:
def randint(a, b):
"Return random integer in range [a, b], including both end points."
return a + randbelow(b - a + 1)
def randbelow(n):
"Return a random int in the range [0,n). Raises ValueError if n<=0."
if n <= 0:
raise ValueError
k = n.bit_length()
numbytes = (k + 7) // 8
while True:
r = int.from_bytes(random_bytes(numbytes), 'big')
r >>= numbytes * 8 - k
if r < n:
return r
def random_bytes(n):
"Return n random bytes"
with open('/dev/urandom', 'rb') as file:
return file.read(n)
Example:
print(randint(1, 1000))
You could also implement random_bytes() using PRNG.
There are many interesting ways to generate randomness without resorting to random (or numpy). For instance, you can use the built in hash function:
def rand_generator(seed, N=10**6, a=0, b=10, integer = True):
'''For a given seed, this function returns N pseudo-random between a and b'''
rands =[]
if integer:
for i in range(N):
num = int(a+(b-a)*(abs(hash(str(hash(str(seed)+str(i+1)))))%10**13)/10**13)
rands.append(num)
return rands
else:
for i in range(N):
num = a+(b-a)*(abs(hash(str(hash(str(seed)+str(i+1)))))%10**13)/10**13
rands.append(num)
return rands
This will generate a list of uniformly distributed pseudo-random numbers between 0 and 1. Like the random number generators in random and numpy the sequence is fully deterministic for a given seed.
Histogram of the function's output.
This algorithm is in no way cryptographically safe but it should be sufficient to answer your question.
If storing the entire list undesirable then the same idea can take the form of a generator expression. After setting the values for a, b, N, and seed as above:
randnum = (int(a+(b-a)*(abs(hash(str(hash(str(seed)+str(j+1))))) % 10**13)/ 10**13) for i in range(N))
is an iterator which generates the next number in the sequence via
>>> next(randnum)
5
This can be made even neater as follows:
def random(seed = None, a=0, b=10, N=10**12, integer=True):
'''Pass a seed to generate new sequence, otherwise next value is returned.'''
if seed:
print("Starting new sequence.")
global _rand_generator
if integer:
hash_plus = lambda j: int(a + (b-a)*(abs(hash(str(hash(str(seed) + str(j+1))))) % 10**13)/ 10**13)
else:
hash_plus = lambda j: a + (b-a)*(abs(hash(str(hash(str(seed) + str(j+1))))) % 10**13)/ 10**13
_rand_generator = (hash_plus(j) for j in range(N))
try:
return next(_rand_generator)
except:
print("Random seed required.")
To generate a random sequence pass a seed to the function.
>>> random(42)
Starting new sequence.
8
Call the function again without a seed to yield the next random number/integer in the sequence.
>>> for i in range(10):
... print(random())
3
4
6
5
5
9
2
2
4
0
To start a new sequence, simply call the function again with a new seed.
>>> random(666)
Starting new sequence.
5
assuming you want integers.
import numpy as np
np.random.randint(1,1000)
#method one, set new seed every time random is needed
seed = "seed" #can be int or string, varies between compilings of the game
possibilities = 3 #0,1,2
hash(str(seed))%possibilities
hash(str(0))%3 #single random number between 0,1,2 (inc) seed is 0
#method two, generate a random list of numbers
size = 100 #length of list
possibilities = 2 #0,1
[hash(str(seed))%possibilities for seed in range(size)]
[hash(str(seed))%5 for seed in range(100)] #generates a list of numbers between 0,1,2,3,4 (inc) with the size of 100
hash(" ")%6
[hash(str(seed))%2 for seed in range(100)].count(1) #validity check. output is ~50
i wrote this program to help with this problem

Categories