Why do the Even Ns take longer than the Odd Ns? - python

I have some code here that solves the n queens problem using backtracking in python. When I run it, the odds always take much less time than the evens. This is especially evident when the n gets to around 20+. Does anyone know why this is?
import time
global N
N = int(input("Enter N: "))
def display_solution(board):
print('\n'.join(['\t'.join([str(cell) for cell in row]) for row in
board]))
def safe(board, row, col):
for i in range(col):
if board[row][i] == 1:
return False
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False
for i, j in zip(range(row, N, 1), range(col, -1, -1)):
if board[i][j] == 1:
return False
return True
def solve_help(board, col):
if col >= N:
return True
for i in range(N):
if safe(board, i, col):
board[i][col] = 1
if solve_help(board, col + 1) is True:
return True
board[i][col] = 0
return False
def solve():
board = [[0 for x in range(N)] for y in range(N)]
if solve_help(board, 0) is False:
print("Solution does not exist")
return False
display_solution(board)
return True
start = time.clock()
solve()
end = time.clock()
print(N, "\t", end-start)
I'm assuming it must have something to do with the diagonals being different for odds as opposed to evens. I'm also not sure if this is an issue with all backtracking algorithms for this problem, or just this specific code. Either way, thanks for the help.

The algorithm takes considerable more time when in one of the first columns backtracking occurs and a next row must be tried. And comparing odd-N boards with N-1 boards shows indeed that often the solution for the even board needs to do more such backtracking/try-next processing. For example the top-left corner of the solution for N=19 looks like this:
1 0 0 0 0
0 0 0 1 0
0 1 0 0 0
0 0 0 0 1*
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
These 5 queens in the first five columns are found quickly, as they are the first that do not collide with the previous queens. And apparently the other queens can be placed without having to reconsider these first five queens.
For N=18 that same corner of the solution looks like this:
1 0 0 0 0
0 0 0 1 0
0 1 0 0 0
0 0 0 0 0-
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 1*
Note the position marked with a minus. This one looks promising (as it was for the 19-board): its investigation takes some considerable time before the conclusion is drawn that the other queens cannot be placed correctly. This early failure costs.
Thus the solution for the 19 board is found sooner than for the 18 board.
Note that the solution for 27 takes slightly more time than the one for 26, although this is not significant: it looks like the time complexity is O(2n), and so to compare times of different board sizes it would better be done on a logarithmic Y-axis:
"work" represents the number of times the function safe is executed.
Whether this algorithm always takes relatively more time for even boards (compared to the time needed for a N+1 board) is unclear, but for these few board sizes it seems related to the knight-jumps that are naturally formed by this algorithm, starting from the top-left corner. Note how this pattern works out perfectly for board sizes 5 and 7: the first spot where the next queen can sit without interfering with the previous positioned queens is always part of the solution. While for board sizes 4 and 6 there isn't even any solution with a queen in a corner, which is the starting point of this algorithm.
Alternatives
To take this question from a programmer's point of view, there is one remedy whereby the difference will (on average) evaporate: pick the columns in a different (or even random) order. It turns out that taking the normal order is one of the less efficient ways to the get a solution.
Shift Pick
A simple shift in this algorithm, where you do not consider the first two rows unless all other fail, already changes the stats considerably:
In solve_help change this:
for i in range(N):
to:
for i in range(N):
i = (i + 2) % N
See how now the average performance has improved: all measures of log(work)/n are below 1, except for n=6. But also: the peeks are now more often for odd values of N.
Random pick
for i in random.sample(range(N), N):
Here is how one such random run turned out:
Much better stats than the original order! Of course, you will get a bad stat now and then, ... because it is random. But on average it performs (much) better.
Other ways of non-random order could include col, and N//2 with differing coefficients, like i = (i + col*2 + N//2) % N, ...etc. But see final remark below.
Other remarks
I would apply the following optimisation: keep track of which rows, forward "diagonals" and backward "diagonals" are already taken. You can use list(s) or set(s) for that. Note that two cells are in the same forward diagonal if the sum of their coordinates are equal. Cells on the backward diagonals have in common that the difference of their coordinates is equal. That way you don't have to scan for a "1" in these lines each time.
Also, board could be just a list of column numbers. There is no need to store all those zeroes. Keep that for the display only.
Finally, there are simple ways to get a solution in linear time. See Wikipedia.

Related

Moves to make all the elements in a array equal in Python

I came across a problem in which the input is as follows:
5
1 1 1 1 6
And the expected output is 4
Basically what we are trying to do is print the minimum number of moves it will require to make all the 5 values equal to each other. One move means reducing a location and incrementing another location. If it is not possible to make them all equal, we print -1.
I tried the below approach:
def solution(N, W):
counter=0
W.sort()
k=W[int(N/2)]
for i in range(0,N):
counter=counter+abs(W[i]-k)
if N%2==0:
tempC=0
k=W[int(N/2)-1]
for i in range(0,N):
tempC=tempC+abs(A[i]-k)
counter=min(counter,tempC)
return counter
and am getting 5 as the answer. Kindly share what your function to achieve this would be.
Lets see how logic works with your input.
5 1 1 1 1 6
1. If 1 2 1 3 3 this case possible then finally it show look like this 2 2 2 2 2. What are the things we get from this result Sum(INITAL_LIST) is equal to SUM(FINAL_LIST), this is 1st condition, if this hold pattern is possible.
2. Among all index-value some of them is going to leave some value some will take. Decrement of one-index and Increment of another-index is taken as one step, so we will consider only decrement case. Those who are leaving and finally become equal to 2. So total-step is equal to some of decremented index value.
Here I use vectorization properties of numpy for easy operation.
CODE :
import numpy as np
def solution(N, W):
if sum(W)%N !=0:
return -1
eq = sum(W)//N
step = np.array(W)-eq
step_sum = np.sum(step[step>0])
return step_sum
if __name__ == '__main__':
var_, list_ = input().split(maxsplit=1)
print(solution(int(var_), list(int(i) for i in list_.split())))
WITHOUT NUMPY :
Update :
step = np.array(W)-eq
step_sum = np.sum(step[step>0])
To :
step = [i-eq for i in W]
step_sum = sum(i for i in step if i>0)
OUTPUT :
5 1 1 1 1 6
4

Compress long vector consists of 0 and 1

Say I got a vector of size [1 x 300], in which each element consists of 0 or 1. I might need to store a bunch of these iteratively during the run time. How do I effectively represent it so that I can effeciently store them (python)?
I guess there are two ways to do it. The first method is to do something like a bitmap (do they even have this in python)?
The second approach
I was thinking maybe is to store the 1's position.
eg. [0, 1, 1, 1]. I will store them as [1,2,3].
Any ideas?
An alternative often used in raster filled shapes processing (where you typically have large uniform areas) is to store your data as spans, i.e. store just the length of each run of 0s or 1s (essentially, it's RLE with the item of each run implicit in the position). You can choose arbitrarily that the first value (and so, all even values) represents a run of 0s, while the second (and so, all odd values) a run of 1s. So, something like
0 0 0 0 0 1 1 0 0 0 1 1 1 1
becomes
5 2 3 4
Appending to such a structure is trivial:
def append(l, value):
cur = (len(l) + 1) % 2
if value == cur:
l[-1] += 1
else:
l.append(1)

Dynamic programming stack of plates [duplicate]

I've been given a little brainteaser to solve.
The task is to make a function with a single integer parameter. You have to figure out how many different combination of tower patterns you can make with that given amount of bricks (each proceeding tower must be less in height than one previous, kind of like). There must be 2 or more towers, one right next to the other.
For example, if you were given 3 blocks you can only produce 1 combination of towers, one with a height of 2 and its neighbor having a height of 1:
|
| |
2 1
Given 4 you can only still produce one combination since the next tower must be shorter than the previous:
|
|
| |
3 1
Given 5 you can produce 2 combinations:
|
|
|
| |
4 1
|
| |
| |
3 2
I have a function that can do all of this, however they give the example that 200 blocks should produce 487067745. Which my function simply does not do. I don't know what I am doing wrong. A push in the right direction would be very much appreciated. My function now looks like this:
def answer(num):
# divide the blocks so we have two towers, one with a height of n-1 and
# the other with a height of one
l1 = num-1
l2 = 1
combinations = 0
while True:
if l1 > l2:
# add 1 to the combinations along with how many combinations we
# can make using the blocks from tower two
combinations += 1 + answer(l2)
elif l1 == l2:
# see if we can make additional towers out of the rightmost tower
# and add that to the combinations
combinations += answer( l2 )
else:
# if the first tower is smaller than or equal to the other tower
# then we stop trying to make combinations
return combinations
l1 -= 1
l2 += 1
While this method does work for smaller numbers of bricks (returning 2 combinations for 5 blocks and 1 combination for 3 or 4 blocks), it does not work for much larger numbers that would be impossible to do on sheets of paper.
Wikipedia gives the generating function for the number of partitions of n with distinct parts as q(n) = product (1+x^k) for k=1..infinity. Given that you exclude the possibility of a single tower, the number of different valid tower arrangements is q(n)-1.
This gives this neat O(n^2) time and O(n) space program for counting tower arrangements.
def towers(n):
A = [1] + [0] * n
for k in xrange(1, n+1):
for i in xrange(n, k-1, -1):
A[i] += A[i-k]
return A[n] - 1
print towers(200)
The output is as required:
487067745
To understand the code, one can observe that A stores the first n+1 coefficients of the generating function product(1+x^k) for k=1...infinity. Each time through the k loop we add one more term to the product. We can stop at n rather than infinity, because subsequent terms of the product do not affect the first n+1 coefficients.
Another, more direct, way to think about the code is to define T(i, k) to be the number of tower combinations (including the single tower) with i blocks, and where the maximum height of any tower is k. Then:
T(0, 0) = 1
T(i, 0) = 0 if i > 0
T(i, k) = T(i, k-1) if i < k
= T(i, k-1) + T(i-k, k-1) if i >= k
Then one can observe that after j iterations of the for k loop, A contains the values of T(j, i) for i from 0 to n. The update is done somewhat carefully, updating the array from the end backwards so that results are changed only after they are used.
Imagine calling the function answer(6). Your code returns 2, the correct answer however is 3 (5, 1; 4, 2; 3, 2, 1). Why is this? your code stops when the amount of blocks above the bottom tower is greater than the length of the bottom tower, so it sees 3, 3 and stops, it therefor never considers the combination 3, 2, 1.
My advice would be to rethink the function, try to take into account the idea that you can stack a number of blocks N on top of a tower that is less than N high.

Brick Tower Building Puzzle

I've been given a little brainteaser to solve.
The task is to make a function with a single integer parameter. You have to figure out how many different combination of tower patterns you can make with that given amount of bricks (each proceeding tower must be less in height than one previous, kind of like). There must be 2 or more towers, one right next to the other.
For example, if you were given 3 blocks you can only produce 1 combination of towers, one with a height of 2 and its neighbor having a height of 1:
|
| |
2 1
Given 4 you can only still produce one combination since the next tower must be shorter than the previous:
|
|
| |
3 1
Given 5 you can produce 2 combinations:
|
|
|
| |
4 1
|
| |
| |
3 2
I have a function that can do all of this, however they give the example that 200 blocks should produce 487067745. Which my function simply does not do. I don't know what I am doing wrong. A push in the right direction would be very much appreciated. My function now looks like this:
def answer(num):
# divide the blocks so we have two towers, one with a height of n-1 and
# the other with a height of one
l1 = num-1
l2 = 1
combinations = 0
while True:
if l1 > l2:
# add 1 to the combinations along with how many combinations we
# can make using the blocks from tower two
combinations += 1 + answer(l2)
elif l1 == l2:
# see if we can make additional towers out of the rightmost tower
# and add that to the combinations
combinations += answer( l2 )
else:
# if the first tower is smaller than or equal to the other tower
# then we stop trying to make combinations
return combinations
l1 -= 1
l2 += 1
While this method does work for smaller numbers of bricks (returning 2 combinations for 5 blocks and 1 combination for 3 or 4 blocks), it does not work for much larger numbers that would be impossible to do on sheets of paper.
Wikipedia gives the generating function for the number of partitions of n with distinct parts as q(n) = product (1+x^k) for k=1..infinity. Given that you exclude the possibility of a single tower, the number of different valid tower arrangements is q(n)-1.
This gives this neat O(n^2) time and O(n) space program for counting tower arrangements.
def towers(n):
A = [1] + [0] * n
for k in xrange(1, n+1):
for i in xrange(n, k-1, -1):
A[i] += A[i-k]
return A[n] - 1
print towers(200)
The output is as required:
487067745
To understand the code, one can observe that A stores the first n+1 coefficients of the generating function product(1+x^k) for k=1...infinity. Each time through the k loop we add one more term to the product. We can stop at n rather than infinity, because subsequent terms of the product do not affect the first n+1 coefficients.
Another, more direct, way to think about the code is to define T(i, k) to be the number of tower combinations (including the single tower) with i blocks, and where the maximum height of any tower is k. Then:
T(0, 0) = 1
T(i, 0) = 0 if i > 0
T(i, k) = T(i, k-1) if i < k
= T(i, k-1) + T(i-k, k-1) if i >= k
Then one can observe that after j iterations of the for k loop, A contains the values of T(j, i) for i from 0 to n. The update is done somewhat carefully, updating the array from the end backwards so that results are changed only after they are used.
Imagine calling the function answer(6). Your code returns 2, the correct answer however is 3 (5, 1; 4, 2; 3, 2, 1). Why is this? your code stops when the amount of blocks above the bottom tower is greater than the length of the bottom tower, so it sees 3, 3 and stops, it therefor never considers the combination 3, 2, 1.
My advice would be to rethink the function, try to take into account the idea that you can stack a number of blocks N on top of a tower that is less than N high.

Iterating over a large list

I have a list in the range [465868129, 988379794] both inclusive. When I use the following code I get a Memory Error. What can I do?
r = [465868129, 988379794]
list = [x for x in xrange(r[0], r[1]+1)]
You could iterate over the xrange directly instead of creating a list.
for x in xrange(r[0], r[1] + 1):
...
But iterating over such a large range is a very, very slow way to find squares. The fact that you run out of memory should alert you that a different approach is needed.
A much better way would be to take the square roots of each end point and then iterate between the square roots. Each integer between the square roots, when squared, would give you one of the numbers you're searching for.
In fact, if you're clever enough, you can generate all the squares with a single list comprehension and avoid an explicit for loop entirely.
Unless you have a very good reason to store the list items in a list, iterate over the generator instead, that way Python won't need to allocate a lot of memory (causing your Memory Error) to create that list:
init, end = (465868129, 988379794)
items = xrange(init, end + 1)
for item in items:
#Do something with item
To count squares on an arbitrary range consider the following formula:
import math
number_of_squares = int(math.sqrt(end) - math.sqrt(init)) +
op(is_perfect_square(init), is_perfect_square(end))
The is_perfect_square(n) is another problem on its own, so check this post if interested.
The op is used to adjust the number of squares when the init and end of the intervals init (or/and/neither) end are perfect squares. So we need a function with the following characteristics:
Both numbers are perfect squares: Eg: 25,64 => 8 - 5 = 3 (and there are 4 squares on that range). (it should sum 1 more)
End is a perfect square: Eg: 26,64 => 8 - 5 = 3 (There are 3 squares on that range). (it is correct => it should sum 0)
Init is a perfect square: Eg: 25,65 => 8 - 5 = 3 (There are 4 squares on that range). (it should sum 1 more)
None of the numbers are primes: Eg: 26, 65 => 8 - 5 = 3 (There are 3 squares on that range) (it is correct => it should sum 0)
So we need an operator with the following characteristics, based on the past examples:
1 op 1 = 1 (Both numbers are perfect squares)
0 op 1 = 0 (End is a perfect square)
1 op 0 = 1 (Init is a perfect square)
0 op 0 = 0 (None of the numbers are perfect squares)
Note that the max function almost fulfils our needs, but it fails on the second case max(0,1) = 1 and it should be 0.
So, looks like the result only depends on the first operator: if it's one, the result is 1, on the other hand if it's 0, it returns 0.
So, it's easy to write the function with that in mind:
import math
number_of_squares = int(math.sqrt(end) - math.sqrt(init)) +
int(is_perfect_square(init))
Thanks to #kojiro, we have this approach (having a similar idea), which is easier to read:
from math import sqrt, floor, ceil
number_of_squares = 1 + floor(sqrt(end)) - ceil(sqrt(init))

Categories