Related
Say I have a circular list which would look like this to a human:
How can I determine whether two indices are adjacent please?
So far I have:
def is_next_to(a, b):
if a == b:
return False
return abs(a - b) == 1
assert is_next_to(1, 1) is False
assert is_next_to(1, 2) is True
assert is_next_to(0, 1) is True
assert is_next_to(5, 0) is True
assert is_next_to(4, 3) is True
assert is_next_to(3, 4) is True
Do I need to make special cases for (0, 5) or (5, 0), or is there some way to use modular arithmetic to solve this?
In a circle of 6, the number 5 is a neighbor of 0, but in a circle of 8, the number 5 would not be a neighbor of 0. So you can only reliable determine this when you know the size of the circle: this should be an extra parameter to your function.
Once you have that, you can use this:
def is_next_to(n, a, b):
return abs(a - b) == 1 or a + b == n - 1
n = 6
assert is_next_to(n, 1, 1) is False
assert is_next_to(n, 1, 2) is True
assert is_next_to(n, 0, 1) is True
assert is_next_to(n, 5, 0) is True
assert is_next_to(n, 4, 3) is True
assert is_next_to(n, 3, 4) is True
With modular arithmetic it would look like this:
return (a + 1) % n == b or (b + 1) % n == a
or:
return (a - b) % n in (1, n - 1)
Given two numbers m and n, in one move you can get two new pairs:
m+n, n
m, n+m
Let's intially set m = n = 1 find the minimum number of moves so that at least one of the numbers equals k
it's guaranteed there's a solution (i.e. there exist a sequence of moves that leads to k)
For example:
given k = 5
the minimum number of moves so that m or n is equal to k is 3
1, 1
1, 2
3, 2
3, 5
Total of 3 moves.
I have come up with a solution using recursion in python, but it doesn't seem to work on big number (i.e 10^6)
def calc(m, n, k):
if n > k or m > k:
return 10**6
elif n == k or m == k:
return 0
else:
return min(1+calc(m+n, n, k), 1+calc(m, m+n, k))
k = int(input())
print(calc(1, 1, k))
How can I improve the performance so it works for big numbers?
Non-Recursive Algorithm based on Priority Queue (using Heap)
State: (sum_, m, n, path)
sum_ is current sum (i.e. m + n)
m and n are the first and second numbers
path is the sequence of (m, n) pairs to get to the current sum
In each step there are two possible moves
Replace first number by the sum
Replace second number by the sum
Thus each state generates two new states. States are prioritized by:
moves: states with a lower number of have higher priority
sum: States with higher sums have higher priority
We use a Priority Queue (Heap in this case) to process states by priority.
Code
from heapq import heappush, heappop
def calc1(k):
if k < 1:
return None, None # No solution
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves, [(m, n)]
h = [] # Priority queue (heap)
path = [(m, n)]
sum_ = m + n
# Python's heapq acts as a min queue.
# We can order thing by max by using -value rather than value
# Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
heappush(h, (moves+1, -sum_, sum_, n, path))
heappush(h, (moves+1, -sum_, m, sum_, path))
while h:
# Get state with lowest sum
moves, sum_, m, n, path = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves, path # Found solution
if sum_ < k:
sum_ = m + n # new sum
# Replace first number with sum
heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
# Replace second number with sum
heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
# else:
# so just continues since sum_ > k
# Exhausted all options, so no solution
return None, None
Test
Test Code
for k in [5, 100, 1000]:
moves, path = calc1(k)
print(f'k: {k}, Moves: {moves}, Path: {path}')
Output
k: 5, Moves: 3, Path: [(1, 1), (2, 3), (2, 5)]
k: 100, Moves: 10, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(8, 19), (27, 19), (27, 46), (27, 73), (27, 100)]
k: 1000, Moves: 15, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(19, 11), (19, 30), (49, 30), (79, 30), (79, 109),
(188, 109), (297, 109), (297, 406), (297, 703), (297, 1000)]
Performance Improvement
Following two adjustments to improve performance
Not including path just number of steps (providing 3X speedup for k = 10,000
Not using symmetric pairs (provided 2x additional with k = 10, 000
By symmetric pairs, mean pairs of m, n which are the same forward and backwards, such as (1, 2) and (2, 1).
We don't need to branch on both of these since they will provide the same solution step count.
Improved Code
from heapq import heappush, heappop
def calc(k):
if k < 1:
return None, None
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves
h = [] # Priority queue (heap)
sum_ = m + n
heappush(h, (moves+1, -sum_, sum_, n))
while h:
moves, sum_, m, n = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves
if sum_ < k:
sum_ = m + n
steps = [(sum_, n), (m, sum_)]
heappush(h, (moves+1, -sum_, *steps[0]))
if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
heappush(h, (moves+1, -sum_, *steps[1]))
Performance
Tested up to k = 100, 000 which took ~2 minutes.
Update
Converted solution by #גלעדברקן from JavaScript to Python to test
def g(m, n, memo):
key = (m, n)
if key in memo:
return memo[key]
if m == 1 or n == 1:
memo[key] = max(m, n) - 1
elif m == 0 or n == 0:
memo[key] = float("inf")
elif m > n:
memo[key] = (m // n) + g(m % n, n, memo)
else:
memo[key] = (n // m) + g(m, n % m, memo)
return memo[key]
def f(k, memo={}):
if k == 1:
return 0
return min(g(k, n, memo) for n in range((k // 2) + 1))
Performance of #גלעדברקן Code
Completed 100K in ~1 second
This is 120X faster than my above heap based solution.
This is an interesting problem in number theory, including linear Diophantine equations. Since there are solutions available on line, I gather that you want help in deriving the algorithm yourself.
Restate the problem: you start with two numbers characterized as 1*m+0*n, 0*m+1*n. Use the shorthand (1, 0) and (0, 1). You are looking for the shortest path to any solution to the linear Diophantine equation
a*m + b*n = k
where (a, b) is reached from starting values (1, 1) a.k.a. ( (1, 0), (0, 1) ).
So ... starting from (1, 1), how can you characterize the paths you reach from various permutations of the binary enhancement. At each step, you have two choices: a += b or b += a. Your existing algorithm already recognizes this binary search tree.
These graph transitions -- edges along a lattice -- can be characterized, in terms of which (a, b) pairs you can reach on a given step. Is that enough of a hint to move you along? That characterization is the key to converting this problem into something close to a direct computation.
We can do much better than the queue even with brute force, trying each possible n when setting m to k. Here's JavaScript code, very close to Python syntax:
function g(m, n, memo){
const key = m + ',' + n;
if (memo[key])
return memo[key];
if (m == 1 || n == 1)
return Math.max(m, n) - 1;
if (m == 0 || n == 0)
return Infinity;
let answer;
if (m > n)
answer = Math.floor(m / n) + g(m % n, n, memo);
else
answer = Math.floor(n / m) + g(m, n % m, memo);
memo[key] = answer;
return answer;
}
function f(k, memo={}){
if (k == 1)
return 0;
let best = Infinity;
for (let n=1; n<=Math.floor(k/2); n++)
best = Math.min(best, g(k, n, memo));
return best;
}
var memo = {};
var ks = [1, 2, 5, 6, 10, 100, 1000, 100000];
for (let k of ks)
console.log(`${ k }: ${ f(k, memo) }`);
By default, in Python, for a recursive function the recursion limit is set to 10^4. You can change it using sys module:
import sys
sys.setrecursionlimit(10**6)
I'm writing a program that reads integer values from an input file, divides the numbers, then writes percentages to an output file. Some of the values that my program may be zero, and bring up a 0/0 or 4/0 occasion.
From here, I get a Zerodivisionerror, is there a way to ignore this error so that it simply prints 0%??
Thanks.
The easiest option is to check if divisor is 0 and if that's the case skip the division and just use 0 as result:
n = [(1, 3), (5, 2), (0, 0), (3, 3), (4, 0)]
res = [x / float(y) if y else 0 for x, y in n] # [0.3333333333333333, 2.5, 0, 1.0, 0]
Other option is to catch ZeroDivisionError with try/except:
res = []
for x, y in n:
try:
res.append(x / float(y))
except ZeroDivisionError:
res.append(0)
I want to write a bottom up fibonacci using O(1) space. My problem is python's recursion stack is limiting me from testing large numbers. Could someone provide an alternate or optimization to what I have? This is my code:
def fib_in_place(n):
def fibo(f2, f1, i):
if i < 1:
return f2
else:
return fibo(f1, f2+f1, i -1)
return fibo(0, 1, n)
Using recursion this way means you're using O(N) space, not O(1) - the O(N) is in the stack.
Why use recursion at all?
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
You can memoize the Fibonacci function for efficiency, but if you require a recursive function, it's still going to take at least O(n):
def mem_fib(n, _cache={}):
'''efficiently memoized recursive function, returns a Fibonacci number'''
if n in _cache:
return _cache[n]
elif n > 1:
return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
return n
This is from my answer on the main Fibonacci in Python question: How to write the Fibonacci Sequence in Python
If you're allowed to use iteration instead of recursion, you should do this:
def fib():
a, b = 0, 1
while True: # First iteration:
yield a # yield 0 to start with and then
a, b = b, a + b # a will now be 1, and b will also be 1, (0 + 1)
usage:
>>> list(zip(range(10), fib()))
[(0, 0), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 8), (7, 13), (8, 21), (9, 34)]
If you just want to get the nth number:
def get_fib(n):
fib_gen = fib()
for _ in range(n):
next(fib_gen)
return next(fib_gen)
and usage
>>> get_fib(10)
55
Why use iteration at all?
def fib(n):
phi_1 = (math.sqrt(5) + 1) / 2
phi_2 = (math.sqrt(5) - 1) / 2
f = (phi_1**n - phi_2**n) / math.sqrt(5)
return round(f)
The algebraic result is exact; the round operation is only to allow for digital representation inaccuracy.
Tail-recursive definitions are easily turned into iterative definitions. If necessary, flip the condition so that the tail-recursive call is in the 'if' branch.
def fibo(f2, f1, i):
if i > 0:
return fibo(f1, f2+f1, i -1)
else:
return f2
Then turn 'if' into 'while', replace return with unpacking assignment of the new arguments, and (optionally) drop 'else'.
def fibo(f2, f1, i):
while i > 0:
f2, f1, i = f1, f2+f1, i -1
return f2
With iteration, you do not need the nested definition.
def fib_efficient(n):
if n < 0:
raise ValueError('fib argument n cannot be negative')
new, old = 0, 1
while n:
new, old = old, old+new
n -= 1
return new
Local names 'new' and 'old' refer to Fibonacci's use of biological reproduction to motivate the sequence. However, the story works better with yeast cells instead of rabbits. Old, mature yeast cells reproduce by budding off new, immature cells. (The original source of the function in India appears to be Virahanka counting the number a ways to make a Sanskrit poetic line with n beats from an ordered sequence of 1- and 2-beat syllables.)
I'm working on a project to build a perfect maze.
I have a class Maze and a class Cell which represents each square in a maze. In my Cell class, I have four Boolean variable(north, south, east, west) to represent whether there is a wall at north or south.. of cell. There is also a boolean variable named visit to check if the cell has been visited. Here is my code for the init() for Cell class.
def __init__(self):
self.north = True
self.south = True
self.east = True
self.west = True
self.visit = False
And for the Maze class, I have self.maze(a bunch of Cell) and the self.size = N(build a N*N maze).
Here is the init() for class Maze:
def __init__(self, N):
self.size = N
self.maze = [[i for i in range(N + 2)] for i in range(N + 2)]
for r in range(self.size + 2):
for c in range(self.size + 2):
self.maze[r][c] = Cell()
While I'm updating the index of maze, I wrote two function to check whether the newX and newY is in the range of 1 <= x <= self.size and 1 <= y <= self.size, also whether the cell has been visited.
Here is the code:
def in_range(self, x, y):
if 1 <= x <= self.size and 1 <= y <= self.size:
return True
else:
return False
def is_valid(self, x, y):
if not self.maze[x][y].getVisit() and self.in_range(x,y):
return True
else:
return False
After all this, I wrote the main structure:
def walk(self, s, x, y):
neighbor = [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]
if s.size() == self.size**2: return
else:
while True:
new = choice(neighbor)#choice() is import from random
#print(self.is_valid(new[0], new[1]))
if self.is_valid(new[0], new[1]):break
else:
if len(neighbor) != 0:
neighbor.remove(new)
new = choice(neighbor)
else:
temp = s.pop(s)
self.walk(s, temp[0], temp[1])
break
print(new)
However, running my code stills give me index that is not between 1 and self.size. I couldn't figure out why, I think my checking algorithm works fine.
Here is what I got:
>>> ================================ RESTART
================================
>>>
>>> a = Maze(5)
>>> a.search()
1 2
(1, 3)
(2, 3)
(2, 4)
(1, 4)
(2, 4)
(2, 5)
(3, 5)
(4, 5)
(4, 4)
(4, 3)
(3, 3)
(3, 2)
(2, 2)
(2, 1)
(1, 1)
(0, 1)
(-1, 1)
(0, 1)
(1, 1)
(0, 1)
(-1, 1)
(-1, 2)
(-1, 1)
(0, 1)
Can someone help me out? plz, really appreciate!
In Python range starts with 0 if you don't specify another start position. Thus, when you initialize your maze, you have cells in (0, *) and (*, 0):
for r in range(self.size + 2):
for c in range(self.size + 2):
self.maze[r][c] = Cell()
When you compute your neighbors you don't check that you don't get out:
neighbor = [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]
And it's only after having assigned new to such a coordinates that you check it is valid. But then, if it is not valid and there is no neighbors, you're calling walk again. You should try to check for in_range independently of visit.
Also your loop can only run once because either new is valid and you break out of the loop or it is not valid and you break out of the loop.
Finally, you forgot to give us the search function and what s is (and s.pop(s) looks funny as usually the argument of pop is an index, not the sequence again).
You could have found this by looking closely at walk(), or splattering it with print statements to trace things.
Things are going wrong at the extremities of your grid (when either x or y == 0 or N-1). Unless you're allowing wraparound, I assume your grid initialization puts walls on the north of the grid(y==0), south, east and west.
By the way, your grid has two unnecessary rows and columns: if you want N cells, numbered O..(N-1), then use range(N), not range(N+2), Or else use range(1,N+1). (Do you pad any unneeded cells with valid==False?)
Anyway, walk() first picks a random neighbor cell and breaks out of the while-loop if it is a valid cell. But if it isn't, it cheerfully picks any remaining neighbor cell (regardless whether valid) and soldiers on. That's your bug. Always test validity.
If it has run out of valid neighbor cells, it backtracks by doing s.pop(s). s.pop(s) also looks like a bug. Why not s.pop()?
Btw, call the variable path instead of s.
Also, this is only checking the coords for validity; you never actually check if you've visited that cell before, so there's no end condition to your recursive backtracking; you could very easily go in loops.
You could help yourself a lot if you refactored walk() into using a separate generator get_random_valid_neighbor() which returns randomly-chosen neighbors already tested as valid (and None when you run out of neighbors).
def walk(self, s, x, y):
...
while True:
new = choice(neighbor)
if self.is_valid(new[0], new[1]):break # tested for validity
else:
if len(neighbor) != 0:
neighbor.remove(new)
new = choice(neighbor) # BUG: never tested for validity
...