Simulating a random walk in Python - python

I am using Python 3. My code below attempts to simulate N steps of a random walk in 3 dimensions. At each step, a random direction is chosen (north, south, east, west, up, down) with 1/6 probability each and a step of size 1 is taken in that direction. The new location is then printed. The starting location is the origin (0,0).
Even though there are no error messages, the code does not work. We should move only one step either in x, y or z. However, in the output, I see that sometimes I don't move at all or sometimes I move in more than one direction.
Here is my code:
import random
N = 30
n = random.random()
x = 0
y = 0
z = 0
count = 0
while count <= N:
if n < 1/6:
x = x + 1
n = random.random()
if n >= 1/6 and n < 2/6:
y = y + 1
n = random.random()
if n >= 2/6 and n < 3/6:
z = z + 1
n = random.random()
if n >= 3/6 and n < 4/6:
x = x - 1
n = random.random()
if n >= 4/6 and n < 5/6:
y = y - 1
n = random.random()
if n >= 5/6:
z = z - 1
n = random.random()
print("(%d,%d,%d)" % (x,y,z))
count = count + 1
print("squared distance = %d" % (x*x + y*y + z*z))
How do you think I can fix the problem?
Thanks a lot.

You should use elif instead of so many ifs. Every time the if is evaluated, the value of n changes and then may qualify for the next if.

Not only should you use elif, even for performance, but you don't need the multiple n = random.random() statements in the loop -- one will do:
import random
N = 30
x = 0
y = 0
z = 0
for _ in range(N):
n = random.random()
if n < 1/6:
x += 1
elif 1/6 <= n < 2/6:
y += 1
elif 2/6 <= n < 3/6:
z += 1
elif 3/6 <= n < 4/6:
x -= 1
elif 4/6 <= n < 5/6:
y -= 1
elif n >= 5/6:
z -= 1
print(f"({x},{y},{z})") # python 3.6ism
print("squared distance = {}".format(x*x + y*y + z*z))

Regardless the version of Python you are using, you need to implement the answer provided by #cdlane.
If you are using Python 2.X your other problem is that Python is interpreting your numbers as ints. To fix that you need to add . to the denominator i.e.
if n < 1/6.:
instead of
if n < 1/6:
1/6 and other fractions are interpreted as ints - you can check it yourself by typing print 1/6 which will give you 0 or printing the actual type with print type(1/6) - which will yield <type 'int'>.
Because of that when you run your program all your ns will satisfy only the last condition (all will be greater than 0).

Related

Repeating if statement

I am having a problem with my code mapping a random walk in 3D space. The purpose of this code is to simulate N steps of a random walk in 3 dimensions. At each step, a random direction is chosen (north, south, east, west, up, down) and a step of size 1 is taken in that direction. Here is my code:
import random # this helps us generate random numbers
N = 30 # number of steps
n = random.random() # generate a random number
x = 0
y = 0
z = 0
count = 0
while count <= N:
if n < 1/6:
x = x + 1 # move east
n = random.random() # generate a new random number
if n >= 1/6 and n < 2/6:
y = y + 1 # move north
n = random.random() # generate a new random number
if n >= 2/6 and n < 3/6:
z = z + 1 # move up
n = random.random() # generate a new random number
if n >= 3/6 and n < 4/6:
x = x - 1 # move west
n = random.random() # generate a new random number
if n >= 4/6 and n < 5/6:
y = y - 1 # move south
n = random.random() # generate a new random number
if n >= 5/6:
z = z - 1 # move down
n = random.random() # generate a new random number
print("(%d,%d,%d)" % (x,y,z))
count = count + 1
print("squared distance = %d" % (x*x + y*y + z*z))
The problem is I am getting more than a single step between each iteration. I've added comments showing the difference in steps between iterations.
Here are the first 10 lines of the output:
(0,-1,0) #1 step
(0,-2,0) #1 step
(1,-3,1) #4 steps
(1,-4,1) #1 step
(1,-3,1) #1 step
(1,-2,1) #1 step
(2,-2,0) #2 steps
(2,-2,0) #0 steps
(2,-2,0) #0 steps
(2,-1,0) #1 step
If you remove the multiple n = random.random() from within the if statements and replace by a single n = random.random() at start of the while loop then there will be only one step per loop.
The issues lies with this line in each of your if statement : n = random.random(). Basically you should had used if and else if so if in a single iteration one if has been executed then it does not get executed again. Also you can add this line at the end of the all if statements instead of repeating it.n = random.random()
Replacing multiple if statements with chain of if-elif-else solved the problem. Also, you could iterate using for instead of while. And in addition - you don't need to regenerate n under each if.
import random # this helps us generate random numbers
N = 30 # number of steps
x = 0
y = 0
z = 0
for _ in range(N):
n = random.random() # generate a new random number
if n < 1/6:
x += 1 # move east
elif 1/6 <= n < 2/6:
y += 1 # move north
elif 2/6 <= n < 3/6:
z += 1 # move up
elif 3/6 <= n < 4/6:
x -= 1 # move west
elif 4/6 <= n < 5/6:
y -= 1 # move south
else:
z -= 1 # move down
print("(%d,%d,%d)" % (x, y, z))
print("squared distance = %d" % (x*x + y*y + z*z))
This is because you are setting n in each if-block which is satisfying next if blocks as well.
Just take out n = random.random() from all if blocks and place it just before end of while loop.
Tip:
To debug this I would've printed the value of n in each if case.

What are the errors inside this random walking code? [duplicate]

This question already has an answer here:
Random walk's weird outcome in python 3?
(1 answer)
Closed 6 years ago.
I am having unexpected outputs with the following code:
import random
N = 30 # number of steps
n = random.random() # generate a random number
x = 0
y = 0
z = 0
count = 0
while count <= N:
if n < 1/3:
x = x + 1 # move east
n = random.random() # generate a new random number
if n >= 1/3 and n < 2/3:
y = y + 1 # move north
n = random.random() # generate a new random number
if n >= 2/3:
z = z + 1 # move up
n = random.random() # generate a new random number
print("(%d,%d,%d)" % (x,y,z))
count = count + 1
When I run the code, the problem is:
Code output displays 31 coordinates, 1 more than the number of steps (N) variable.
Each iteration for 1 step should take only 1 step but it sometimes take multiple steps.
When I tested the code, the problem is ensured. To test the code, I assigned N = 1, and saw the following output:
(-1,0,1) This should be the initial step, but it took multiple steps (both x-1 and z+1), how could this happen?
(-2,0,1) Number of step variable (N) = 1 but this is the second output, why was it displayed?
Thanks for helping
N is 30, so count goes from 0 to 30. Since 30 <= 30 you will run the loop for count=0, 1, ..., 29 AND 30 which is 31 times
When you take a step, you don't ensure that another step is NOT taken. If random happens, you could enter the second or third if after already being in a previous one in the same loop iteration
You are dividing two ints which will only result in another int. So basically your code is do the following:
if n < 0:
x = x + 1 # move east
n = random.random() # generate a new random number
if n >= 0 and n < 1:
y = y + 1 # move north
n = random.random() # generate a new random number
if n >= 1:
z = z + 1 # move up
n = random.random()
fix by changing each if line to include a floating point number
if n < 1.0/3

Probability of finding a prime (using miller-rabin test)

I've implemented Miller-Rabin primality test and every function seems to be working properly in isolation. However, when I try to find a prime by generating random numbers of 70 bits my program generates in average more than 100000 numbers before finding a number that passes the Miller-Rabin test (10 steps). This is very strange, the probability of being prime for a random odd number of less than 70 bits should be very high (more than 1/50 according to Hadamard-de la Vallée Poussin Theorem). What could be wrong with my code? Would it be possible that the random number generator throws prime numbers with very low probability? I guess not... Any help is very welcome.
import random
def miller_rabin_rounds(n, t):
'''Runs miller-rabin primallity test t times for n'''
# First find the values r and s such that 2^s * r = n - 1
r = (n - 1) / 2
s = 1
while r % 2 == 0:
s += 1
r /= 2
# Run the test t times
for i in range(t):
a = random.randint(2, n - 1)
y = power_remainder(a, r, n)
if y != 1 and y != n - 1:
# check there is no j for which (a^r)^(2^j) = -1 (mod n)
j = 0
while j < s - 1 and y != n - 1:
y = (y * y) % n
if y == 1:
return False
j += 1
if y != n - 1:
return False
return True
def power_remainder(a, k, n):
'''Computes (a^k) mod n efficiently by decomposing k into binary'''
r = 1
while k > 0:
if k % 2 != 0:
r = (r * a) % n
a = (a * a) % n
k //= 2
return r
def random_odd(n):
'''Generates a random odd number of max n bits'''
a = random.getrandbits(n)
if a % 2 == 0:
a -= 1
return a
if __name__ == '__main__':
t = 10 # Number of Miller-Rabin tests per number
bits = 70 # Number of bits of the random number
a = random_odd(bits)
count = 0
while not miller_rabin_rounds(a, t):
count += 1
if count % 10000 == 0:
print(count)
a = random_odd(bits)
print(a)
The reason this works in python 2 and not python 3 is that the two handle integer division differently. In python 2, 3/2 = 1, whereas in python 3, 3/2=1.5.
It looks like you should be forcing integer division in python 3 (rather than float division). If you change the code to force integer division (//) as such:
# First find the values r and s such that 2^s * r = n - 1
r = (n - 1) // 2
s = 1
while r % 2 == 0:
s += 1
r //= 2
You should see the correct behaviour regardless of what python version you use.

Syntax - Assigning a variable then putting it in while loop doesn't work. Why?

I'm wondering why this won't work:
n = 10
x = 1
while x < n:
x += 1
n += n * (x - 1)
print n
I've already assigned n = 10, placing it in the while loop should be like placing a 10 there. Running it in Terminal acts like I've placed a raw_input() there.
This works:
n = 10
x = 1
while x < 10:
x += 1
n += n * (x - 1)
print n
It looks the same to me...
Thanks!
In the first example, n, your limit, is increasing (n += n * (x - 1)) and you have an infinite loop. In the second one the limit is constant (10) so it terminates.
Because, in the while loop, for each iteration, you also update n
while x < n:
x += 1
n += n * (x - 1)
So, after the first few iterations, n is no longer 10, and it keeps increasing.
I finally understand it after doing more loops of similar type. The reason is that this creates an infinite loop because in:
n = 10
x = 1
while x < n:
x += 1
n += n * (x - 1)
print n
The n increases along with x:
x += 1
n += n * (x - 1)
So x never catches up with n and causes an infinite loop.
This works because the 10 never increases and x can catch up with it.
n = 10
x = 1
while x < 10:
x += 1
n += n * (x - 1)
print n

Get logarithm without math log python

I need to generate the result of the log.
I know that:
Then I made my code:
def log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
print(log_b)
return int(round(log_b))
But it works very slowly. Can I use other method?
One other thing you might want to consider is using the Taylor series of the natural logarithm:
Once you've approximated the natural log using a number of terms from this series, it is easy to change base:
EDIT: Here's another useful identity:
Using this, we could write something along the lines of
def ln(x):
n = 1000.0
return n * ((x ** (1/n)) - 1)
Testing it out, we have:
print ln(math.e), math.log(math.e)
print ln(0.5), math.log(0.5)
print ln(100.0), math.log(100.0)
Output:
1.00050016671 1.0
-0.692907009547 -0.69314718056
4.6157902784 4.60517018599
This shows our value compared to the math.log value (separated by a space) and, as you can see, we're pretty accurate. You'll probably start to lose some accuracy as you get very large (e.g. ln(10000) will be about 0.4 greater than it should), but you can always increase n if you need to.
I used recursion:
def myLog(x, b):
if x < b:
return 0
return 1 + myLog(x/b, b)
You can use binary search for that.
You can get more information on binary search on Wikipedia:
Binary search;
Doubling search.
# search for log_base(x) in range [mn, mx] using binary search
def log_in_range(x, base, mn, mx):
if (mn <= mx):
med = (mn + mx) / 2.0
y = base ** med
if abs(y - x) < 0.00001: # or if math.isclose(x, y): https://docs.python.org/3/library/math.html#math.isclose
return med
elif x > y:
return log_in_range(x, base, med, mx)
elif x < y:
return log_in_range(x, base, mn, med)
return 0
# determine range using doubling search, then call log_in_range
def log(x, base):
if base <= 0 or base == 1 or x <= 0:
raise ValueError('math domain error')
elif 0 < base < 1:
return -log(x, 1/base)
elif 1 <= x and 1 < base:
mx = 1
y = base
while y < x:
y *= y
mx *= 2
return log_in_range(x, base, 0, mx)
elif 0 <= x < 1 and 1 < base:
mn = -1
y = 1/base
while y > x:
y = y ** 0.5
mn *= 2
return log_in_range(x, base, mn, 0)
import math
try :
number_and_base = input() ##input the values for number and base
##assigning those values for the variables
number = int(number_and_base.split()[0])
base = int(number_and_base.split()[1])
##exception handling
except ValueError :
print ("Invalid input...!")
##program
else:
n1 = 1 ##taking an initial value to iterate
while(number >= int(round(base**(n1),0))) : ##finding the most similer value to the number given, varying the vlaue of the power
n1 += 0.000001 ##increasing the initial value bit by bit
n2 = n1-0.0001
if abs(number-base**(n2)) < abs(base**(n1)-number) :
n = n2
else :
n = n1
print(math.floor(n)) ##output value
Comparison:-
This is how your log works:-
def your_log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
#print log_b
return int(round(log_b))
print your_log(16, 2)
# %timeit %run your_log.py
# 1000 loops, best of 3: 579 us per loop
This is my proposed improvement:-
def my_log(x, base):
count = -1
while x > 0:
x /= base
count += 1
if x == 0:
return count
print my_log(16, 2)
# %timeit %run my_log.py
# 1000 loops, best of 3: 321 us per loop
which is faster, using the %timeit magic function in iPython to time the execution for comparison.
It will be long process since it goes in a loop. Therefore,
def log(x,base):
result = ln(x)/ln(base)
return result
def ln(x):
val = x
return 99999999*(x**(1/99999999)-1)
log(8,3)
Values are nearly equal but not exact.

Categories