Nested loops with different and dependant steps - python

I need to make an algorithm to perform a task with 2 while loops.
I will explain what it is I want to achieve. I need to make a loop based on length of h value. Well the loop should take big steps with f.ex. 3 or 2 or 4, the item from while or for loop should check a condition or equation. I need to check when or which value of item gives zero value to n in equation.
When n value reaches zero, I need to perform a smaller step with 0.1. Because I need to be more precis. There is no need to start the loop with small step in the beginning it slows performance time, and it is just interesting to know exact small value of item which gives zero to n equation. And this is true when n equation closes to zero value.
To illustrate the example:
Loop 1 starts and take big steps.
h = 50
st = 10
loop 1 starts
0 , 3, 6, 9, 12, 15, 18, 21, 24 ………………… #<-------- steps from h value
25.0 , 22.0 , 19.0, 16.0, 13.0, 10.0, 7.0, 4.0, 1.0 ………………… #<-------- calculate values from n equation
If n <= 2 or n>= -2:
|_____ loop 2 starts with:
24.0, 23.9 , 23.8, 23.7, 23.6, …………….. #<-------- small steps iterate with hj = 24+st
0.4, 0.3 , 0.1, 0.0, -1.0, …………….. #<-------- calculate values from n equation
Calculate n values from n equation
M = 2 + n
If (n <= 0 or n>= -1) and M==2 : #<-------- If statement is fullfilled then return some values and quit loop 2 and loop 1.
Return n, itemstep from loop 2, M
Elif (n <= 0 or n>= -1) and M!=2:
Quit loop 2 and 1 #<-------- If statement is not fullfilled then quit loop 2 and loop 1.
The reason for n >= -1, because we could start step from 50 down to 0 and n value can be negative as well.
I have tried to figure it out with code below, but it seems not working, is it a way to generate such code in while or for loop.
M = 2 #<---------- to check conditions
j = 0
st = 10
h = 50 #<---------- to iterate over length h
n = h/2 - j #<---------- to calculate n values (n equation)
result =[]
while j <=h: #<---------- loop 1
n = h/2 - j
if n <= 3 or n >= -3: #<---------- if this is satisified then start loop2
print(n)
i=j
while i < 10+st: #<---------- loop 2
n = h/2-i
M = 2+n
if (n <= 0 or n>= -1) and M==2:
result.append(n, i, M) #<---------- return values
i = 70+j #<---------- stop loop 2 and loop 1
j = h+70
elif (n <= 0 or n>= -1) and M!=2: #<---------- if this is not satisified then quit loop2 and loop 1
i = 70+j #<---------- stop loop 2 and loop 1
j = h+70
i +=0.1
j += i
j += 3

This might help you some. (Not exactly what you wanted but it is a start)
j = 0
h = 50
while j <= h:
n = h/2 - j
if -3 <= n <= 3:
i = j
while i < j + 10:
n = h/2 - i
if n <= 0:
j = h + 70
print(n)
break
i += 0.1
j += 3
or you could do it like this.
j = 0
h = 50
while j <= h:
n = h/2 - j
if -3 <= n <= 3:
break
j += 3
i = j
while i < j + 10:
n = h/2 - i
if n <= 0:
print(n)
break
i += 0.1

Related

Pandas count of sequence of positive and negative numbers

Problem is probably simple, but my brain doesn't work as expected.
Imagine you have this Panda Series:
y = pd.Series([5, 5 , -5 , -10, 7 , 7 ])
z = y * 0
I would like to have output:
1, 2 , -1 ,-2 ,1 ,2
My solution below:
for i, row in y.iteritems():
if i == 0 and y[i] > 0:
z[i] = 1
elif i == 0:
z[i] = -1
elif y[i] >= 0 and y[i-1] >= 0:
z[i] = 1 + z[i-1]
elif y[i] < 0 and y[i-1] < 0:
z[i] = -1 + z[i-1]
elif y[i] >= 0 and y[i-1] < 0:
z[i] = 1
elif y[i] < 0 and y[i-1] >= 0:
z[i] = -1
I would think there is a more Python/Panda solution.
You can use np.sign() to check if the number is positive/negative ans compare it to the next row using shift(). Finally, use cumcount() to sum each sub series
y = pd.Series([5, 5 , -5 , -10, 7 , 7 ])
parts = (np.sign(y) != np.sign(y.shift())).cumsum()
print((y.groupby(parts).cumcount() + 1) * np.sign(y))
# or print(y.groupby(parts).cumcount().add(1).mul(np.sign(y)))
Output
0 1
1 2
2 -1
3 -2
4 1
5 2
Turning points in terms of sign are found via looking at difference not being 0 when subjected to np.sign. Then cumulative sum of this gives consecutive groups of same sign. We lastly put cumcount to number each group and also multiply by the sign to get negative counts:
signs = np.sign(y)
grouper = signs.diff().ne(0).cumsum()
result = y.groupby(grouper).cumcount().add(1).mul(signs)
where add(1) is because cumcount gives 0, 1, .. but we need 1 more.
>>> result
0 1
1 2
2 -1
3 -2
4 1
5 2

Why does program count one extra value (cycle "while")?

Why the program count one more value? For example, I give him N = 50. It gives out:
1
4
9
16
25
36
49
64
Code:
N = int(input())
n = 1
k = 1
while n < N:
n = k ** 2
print(n)
k = k + 1
As explained, you're checking n then changing n, you want to change n then check before continuing.
You can use the walrus operator to assign n and check it's value all in the while statement. (requires Python 3.8+)
N = int(input())
n = 1
k = 1
while (n := k**2) < N:
print(n)
k += 1
This essentially assigns n to k**2 then checks if that result is <N before continuing.
1
4
9
16
25
36
49
Your program outputs 1 4 9 16 25 36 49 64 if your input is 50 because the `while`` loop is checking the value before you increase it. Once in the loop, you increase it, calculate the square and then print.
If you want it to terminate, try setting calculating n as the last step in the loop:
N = int(input())
n = 1
k = 1
while n < N:
print(n)
k = k + 1
n = k ** 2
You're checking whether you reached the limit before you calculate the square and print it. So you're checking the previous value of n, not the one that's about to be printed.
Move the check inside the loop.
while True:
n = k ** 2
if n >= N:
break
print(n)
k += 1
The n < N is evaluated after you've changed (and printed) n.
n = 1
k = 1
N=50
while 1:
n = k ** 2
if n > N:
break
print(n)
k = k + 1
To fix this, break before you print, moving the evaluation inside the loop rather than after the last update of n
1
4
9
16
25
36
49
With the condition of your code, for example, when n = 49, The condition is fulfilled because 49 < 50 therefore it will continue to process the value and print the new one. But once n = 64 which is > 50, it stops. This is a possible solution:
N = int(input())
n = 1
k = 1
while True:
if n >= N:
break
n = k ** 2
print(n)
k = k + 1
This will continuously execute the code but once the condition is met that n >= N, it will stop executing.

Finding a Pattern in a Grid Python [duplicate]

This question already has answers here:
Largest rectangle of 1's in 2d binary matrix
(6 answers)
Closed 2 years ago.
I have randomly generated grid containing 0 and 1:
1 1 0 0 0 1 0 1
1 1 1 0 1 1 1 1
1 0 0 0 1 0 1 1
0 0 1 0 1 0 1 1
1 1 1 1 0 0 1 1
0 0 1 1 1 1 1 0
0 1 0 0 1 0 1 1
How can I iterate through the grid to find the largest cluster of 1s, that is equal or larger than 4 items (across row and column)?
I assume I need to keep a count of each found cluster while iterating and ones its more than 4 items, record and count in a list and then find the largest number.
The problem is that I cannot figure out how to do so across both rows and columns and record the count. I have iterated through the grid but not sure how to move further than two rows.
For example in the above example, the largest cluster is 8. There are some other clusters in the grid, but they have 4 elements:
A A 0 0 0 1 0 1
A A 1 0 1 1 1 1
1 0 0 0 1 0 1 1
0 0 1 0 1 0 1 1
1 1 B B 0 0 1 1
0 0 B B 1 1 1 0
0 1 0 0 1 0 1 1
The code I tried:
rectcount = []
for row in range(len(grid)):
for num in range(len(grid[row])):
# count = 0
try:
# if grid[row][num] == 1:
# if grid[row][num] == grid[row][num + 1] == grid[row + 1][num] == grid[row + 1][num + 1]:
# count += 1
if grid[row][num] == grid[row][num + 1]:
if grid[row + 1][num] == grid[row][num + 1]:
count += 1
# if grid[row][num] == grid[row][num + 1] and grid[row][num] == grid[row + 1][num]:
# count += 1
else:
count = 0
if grid[row][num] == grid[row + 1][num]:
count += 1
except:
pass
I've implemented three algorithms.
First algorithm is Simple, using easiest approach of nested loops, it has O(N^5) time complexity (where N is one side of input grid, 10 for our case), for our inputs of size 10x10 time of O(10^5) is quite alright. Algo id in code is algo = 0. If you just want to see this algorithm jump to line ------ Simple Algorithm inside code.
Second algorithm is Advanced, using Dynamic Programming approach, its complexity is O(N^3) which is much faster than first algorithm. Algo id in code is algo = 1. Jump to line ------- Advanced Algorithm inside code.
Third algorithm Simple-ListComp I implemented just for fun, it is almost same like Simple, same O(N^5) complexity, but using Python's list comprehensions instead of regular loops, that's why it is shorter, also a bit slower because doesn't use some optimizations. Algo id in code is algo = 2. Jump to line ------- Simple-ListComp Algorithm inside code to see algo.
The rest of code, besides algorithms, implements checking correctness of results (double-checking between algorithms), printing results, producing text inputs. Code is split into solving-task function solve() and testing function test(). solve() function has many arguments to allow configuring behavior of function.
All main code lines are documented by comments, read them to learn how to use code. Basically if s variable contains multi-line text with grid elements, same like in your question, you just run solve(s, text = True) and it will solve task and print results. Also you may choose algorithm out of two versions (0 (Simple) and 1 (Advanced) and 2 (Simple-ListComp)) by giving next arguments to solve function algo = 0, check = False (here 0 for algo 0). Look at test() function body to see simplest example of usage.
Algorithms output to console by default all clusters, from largest to smallest, largest is signified by . symbol, the rest by B, C, D, ..., Z symbols. You may set argument show_non_max = False in solve function if you want only first (largest) cluster to be shown.
I'll explain Simple algorithm:
Basically what algorithm does - it searches through all possible angled 1s rectangles and stores info about maximal of them into ma 2D array. Top-left point of such rectangle is (i, j), top-right - (i, k), bottom-left - (l, j + angle_offset), bottom-right - (l, k + angle_offset), all 4 corners, that's why we have so many loops.
In outer two i (row) , j (column) loops we iterate over whole grid, this (i, j) position will be top-left point of 1s rectangle, we need to iterate whole grid because all possible 1s rectangles may have top-left at any (row, col) point of whole grid. At start of j loop we check that grid at (i, j) position should always contain 1 because inside loops we search for all rectangle with 1s only.
k loop iterates through all possible top-right positions (i, k) of 1s rectangle. We should break out of loop if (i, k) equals to 0 because there is no point to extend k further to right because such rectangle will always contain 0.
In previous loops we fixed top-left and top-right corners of rectangle. Now we need to search for two bottom corners. For that we need to extend rectangle downwards at different angles till we reach first 0.
off loop tries extending rectangle downwards at all possible angles (0 (straight vertical), +1 (45 degrees shifted to the right from top to bottom), -1 (-45 degrees)), off basically is such number that grid[y][x] is "above" (corresponds to by Y) grid[y + 1][x + off].
l tries to extend rectangle downwards (in Y direction) at different angles off. It is extended till first 0 because it can't be extended further then (because each such rectangle will already contain 0).
Inside l loop there is if grid[l][max(0, j + off * (l - i)) : min(k + 1 + off * (l - i), c)] != ones[:k - j + 1]: condition, basically this if is meant to check that last row of rectangle contains all 1 if not this if breaks out of loop. This condition compares two list slices for non-equality. Last row of rectangle spans from point (l, j + angle_offset) (expression max(0, j + off * (l - i)), max-limited to be 0 <= X) to point (l, k + angle_offset) (expression min(k + 1 + off * (l - i), c), min-limited to be X < c).
Inside l loop there are other lines, ry, rx = l, k + off * (l - i) computes bottom-right point of rectangle (ry, rx) which is (l, k + angle_offset), this (ry, rx) position is used to store found maximum inside ma array, this array stores all maximal found rectangles, ma[ry][rx] contains info about rectangle that has bottom-right at point (ry, rx).
rv = (l + 1 - i, k + 1 - j, off) line computes new possible candidate for ma[ry][rx] array entry, possible because ma[ry][rx] is updated only if new candidate has larger area of 1s. Here rv[0] value inside rv tuple contains height of such rectangle, rv[1] contains width of such rectangle (width equals to the length of bottom row of rectangle), rv[2] contains angle of such rectangle.
Condition if rv[0] * rv[1] > ma[ry][rx][0] * ma[ry][rx][1]: and its body just checks if rv area is larger than current maximum inside array ma[ry][rx] and if it is larger then this array entry is updated (ma[ry][rx] = rv). I'll remind that ma[ry][rx] contains info (width, height, angle) about current found maximal-area rectangle that has bottom-right point at (ry, rx) and that has these width, height and angle.
Done! After algorithm run array ma contains information about all maximal-area angled rectangles (clusters) of 1s so that all clusters can be restored and printed later to console. Largest of all such 1s-clusters is equal to some rv0 = ma[ry0][rx0], just iterate once through all elements of ma and find such point (ry0, rx0) so that ma[ry0][rx0][0] * ma[ry0][rx0][1] (area) is maximal. Then largest cluster will have bottom-right point (ry0, rx0), bottom-left point (ry0, rx0 - rv0[1] + 1), top-right point (ry0 - rv0[0] + 1, rx0 - rv0[2] * (rv0[0] - 1)), top-left point (ry0 - rv0[0] + 1, rx0 - rv0[1] + 1 - rv0[2] * (rv0[0] - 1)) (here rv0[2] * (rv0[0] - 1) is just angle offset, i.e. how much shifted is first row along X compared to last row of rectangle).
Try it online!
# ----------------- Main function solving task -----------------
def solve(
grid, *,
algo = 1, # Choose algorithm, 0 - Simple, 1 - Advanced, 2 - Simple-ListComp
check = True, # If True run all algorithms and check that they produce same results, otherwise run just chosen algorithm without checking
text = False, # If true then grid is a multi-line text (string) having grid elements separated by spaces
print_ = True, # Print results to console
show_non_max = True, # When printing if to show all clusters, not just largest, as B, C, D, E... (chars from "cchars")
cchars = ['.'] + [chr(ii) for ii in range(ord('B'), ord('Z') + 1)], # Clusters-chars, these chars are used to show clusters from largest to smallest
one = None, # Value of "one" inside grid array, e.g. if you have grid with chars then one may be equal to "1" string. Defaults to 1 (for non-text) or "1" (for text).
offs = [0, +1, -1], # All offsets (angles) that need to be checked, "off" is such that grid[i + 1][j + off] corresponds to next row of grid[i][j]
debug = False, # If True, extra debug info is printed
):
# Preparing
assert algo in [0, 1, 2], algo
if text:
grid = [l.strip().split() for l in grid.splitlines() if l.strip()]
if one is None:
one = 1 if not text else '1'
r, c = len(grid), len(grid[0])
sgrid = '\n'.join([''.join([str(grid[ii][jj]) for jj in range(c)]) for ii in range(r)])
mas, ones = [], [one] * max(c, r)
# ----------------- Simple Algorithm, O(N^5) Complexity -----------------
if algo == 0 or check:
ma = [[(0, 0, 0) for jj in range(c)] for ii in range(r)] # Array containing maximal answers, Lower-Right corners
for i in range(r):
for j in range(c):
if grid[i][j] != one:
continue
for k in range(j + 1, c): # Ensure at least 2 ones along X
if grid[i][k] != one:
break
for off in offs:
for l in range(i + 1, r): # Ensure at least 2 ones along Y
if grid[l][max(0, j + off * (l - i)) : min(k + 1 + off * (l - i), c)] != ones[:k - j + 1]:
l -= 1
break
ry, rx = l, k + off * (l - i)
rv = (l + 1 - i, k + 1 - j, off)
if rv[0] * rv[1] > ma[ry][rx][0] * ma[ry][rx][1]:
ma[ry][rx] = rv
mas.append(ma)
ma = None
# ----------------- Advanced Algorithm using Dynamic Programming, O(N^3) Complexity -----------------
if algo == 1 or check:
ma = [[(0, 0, 0) for jj in range(c)] for ii in range(r)] # Array containing maximal answers, Lower-Right corners
for off in offs:
d = [[(0, 0, 0) for jj in range(c)] for ii in range(c)]
for i in range(r):
f, d_ = 0, [[(0, 0, 0) for jj in range(c)] for ii in range(c)]
for j in range(c):
if grid[i][j] != one:
f = j + 1
continue
if f >= j:
# Check that we have at least 2 ones along X
continue
df = [(0, 0, 0) for ii in range(c)]
for k in range(j, -1, -1):
t0 = d[j - off][max(0, k - off)] if 0 <= j - off < c and k - off < c else (0, 0, 0)
if k >= f:
t1 = (t0[0] + 1, t0[1], off) if t0 != (0, 0, 0) else (0, 0, 0)
t2 = (1, j - k + 1, off)
t0 = t1 if t1[0] * t1[1] >= t2[0] * t2[1] else t2
# Ensure that we have at least 2 ones along Y
t3 = t1 if t1[0] > 1 else (0, 0, 0)
if k < j and t3[0] * t3[1] < df[k + 1][0] * df[k + 1][1]:
t3 = df[k + 1]
df[k] = t3
else:
t0 = d_[j][k + 1]
if k < j and t0[0] * t0[1] < d_[j][k + 1][0] * d_[j][k + 1][1]:
t0 = d_[j][k + 1]
d_[j][k] = t0
if ma[i][j][0] * ma[i][j][1] < df[f][0] * df[f][1]:
ma[i][j] = df[f]
d = d_
mas.append(ma)
ma = None
# ----------------- Simple-ListComp Algorithm using List Comprehension, O(N^5) Complexity -----------------
if algo == 2 or check:
ma = [
[
max([(0, 0, 0)] + [
(h, w, off)
for h in range(2, i + 2)
for w in range(2, j + 2)
for off in offs
if all(
cr[
max(0, j + 1 - w - off * (h - 1 - icr)) :
max(0, j + 1 - off * (h - 1 - icr))
] == ones[:w]
for icr, cr in enumerate(grid[max(0, i + 1 - h) : i + 1])
)
], key = lambda e: e[0] * e[1])
for j in range(c)
]
for i in range(r)
]
mas.append(ma)
ma = None
# ----------------- Checking Correctness and Printing Results -----------------
if check:
# Check that we have same answers for all algorithms
masx = [[[cma[ii][jj][0] * cma[ii][jj][1] for jj in range(c)] for ii in range(r)] for cma in mas]
assert all([masx[0] == e for e in masx[1:]]), 'Maximums of algorithms differ!\n\n' + sgrid + '\n\n' + (
'\n\n'.join(['\n'.join([' '.join([str(e1).rjust(2) for e1 in e0]) for e0 in cma]) for cma in masx])
)
ma = mas[0 if not check else algo]
if print_:
cchars = ['.'] + [chr(ii) for ii in range(ord('B'), ord('Z') + 1)] # These chars are used to show clusters from largest to smallest
res = [[grid[ii][jj] for jj in range(c)] for ii in range(r)]
mac = [[ma[ii][jj] for jj in range(c)] for ii in range(r)]
processed = set()
sid = 0
for it in range(r * c):
sma = sorted(
[(mac[ii][jj] or (0, 0, 0)) + (ii, jj) for ii in range(r) for jj in range(c) if (ii, jj) not in processed],
key = lambda e: e[0] * e[1], reverse = True
)
if len(sma) == 0 or sma[0][0] * sma[0][1] <= 0:
break
maxv = sma[0]
if it == 0:
maxvf = maxv
processed.add((maxv[3], maxv[4]))
show = True
for trial in [True, False]:
for i in range(maxv[3] - maxv[0] + 1, maxv[3] + 1):
for j in range(maxv[4] - maxv[1] + 1 - (maxv[3] - i) * maxv[2], maxv[4] + 1 - (maxv[3] - i) * maxv[2]):
if trial:
if mac[i][j] is None:
show = False
break
elif show:
res[i][j] = cchars[sid]
mac[i][j] = None
if show:
sid += 1
if not show_non_max and it == 0:
break
res = '\n'.join([''.join([str(res[ii][jj]) for jj in range(c)]) for ii in range(r)])
print(
'Max:\nArea: ', maxvf[0] * maxvf[1], '\nSize Row,Col: ', (maxvf[0], maxvf[1]),
'\nLowerRight Row,Col: ', (maxvf[3], maxvf[4]), '\nAngle: ', ("-1", " 0", "+1")[maxvf[2] + 1], '\n', sep = ''
)
print(res)
if debug:
# Print all computed maximums, for debug purposes
for cma in [ma, mac]:
print('\n' + '\n'.join([' '.join([f'({e0[0]}, {e0[1]}, {("-1", " 0", "+1")[e0[2] + 1]})' for e0_ in e for e0 in (e0_ or ('-', '-', 0),)]) for e in cma]))
print(end = '-' * 28 + '\n')
return ma
# ----------------- Testing -----------------
def test():
# Iterating over text inputs or other ways of producing inputs
for s in [
"""
1 1 0 0 0 1 0 1
1 1 1 0 1 1 1 1
1 0 0 0 1 0 1 1
0 0 1 0 1 0 1 1
1 1 1 1 0 0 1 1
0 0 1 1 1 1 1 0
0 1 0 0 1 0 1 1
""",
"""
1 0 1 1 0 1 0 0
0 1 1 0 1 0 0 1
1 1 0 0 0 0 0 1
0 1 1 1 0 1 0 1
0 1 1 1 1 0 1 1
1 1 0 0 0 1 0 0
0 1 1 1 0 1 0 1
""",
"""
0 1 1 0 1 0 1 1
0 0 1 1 0 0 0 1
0 0 0 1 1 0 1 0
1 1 0 0 1 1 1 0
0 1 1 0 0 1 1 0
0 0 1 0 1 0 1 1
1 0 0 1 0 0 0 0
0 1 1 0 1 1 0 0
"""
]:
solve(s, text = True)
if __name__ == '__main__':
test()
Output:
Max:
Area: 8
Size Row,Col: (4, 2)
LowerRight Row,Col: (4, 7)
Angle: 0
CC000101
CC1011..
100010..
001010..
1BBB00..
00BBBDD0
010010DD
----------------------------
Max:
Area: 6
Size Row,Col: (3, 2)
LowerRight Row,Col: (2, 1)
Angle: -1
10..0100
0..01001
..000001
0BBB0101
0BBB1011
CC000100
0CC10101
----------------------------
Max:
Area: 12
Size Row,Col: (6, 2)
LowerRight Row,Col: (5, 7)
Angle: +1
0..01011
00..0001
000..010
BB00..10
0BB00..0
001010..
10010000
01101100
----------------------------

python: algorithm for swapping elements between two arrays

I am analyzing counting example in python presented by Codility
I don't understand the logic used in the last loop (5 last rows) of this algorithm.
Can anybody help please?
The problem:
You are given an integer m (1 < m < 1000000) and two non-empty,
zero-indexed arrays A and B of n integers, a0, a1, ... ,
an−1 and b0, b1, ... , bn−1 respectively (0 < ai, bi < m).
The goal is to check whether there is a swap operation which can be
performed on these arrays in such a way that the sum of elements in
array A equals the sum of elements in array B after the swap. By
swap operation we mean picking one element from array A and one
element from array B and exchanging them.
The solution:
def counting(A, m):
n = len(A)
count = [0] * (m + 1)
for k in xrange(n):
count[A[k]] += 1
return count
def fast_solution(A, B, m):
n = len(A)
sum_a = sum(A)
sum_b = sum(B)
d = sum_b - sum_a
if d % 2 == 1:
return False
d //= 2
count = counting(A, m)
for i in xrange(n):
if 0 <= B[i] - d and B[i] - d <= m and count[B[i] - d] > 0:
return True
return False
What I would recommend you is read again the explanations given in the exercise. It already explains what how the algorithm works. However, if you still have problems with it, then take a piece of paper, and some very simple example arrays and go through the solution step by step.
For example, let A = [6, 6, 1, 2, 3] and B = [1, 5, 3, 2, 1].
Now let's go through the algorithm.
I assume you understand how this method works:
def counting(A, m):
n = len(A)
count = [0] * (m + 1)
for k in xrange(n):
count[A[k]] += 1
return count
It just returns a list with counts as explained in the exercise. For list A and m = 10 it will return:
[0, 1, 1, 1, 0, 0, 2, 0, 0, 0, 0]
Then we go through the main method fast_solution(A, B, m):
n = 11 (this will be used in the loop)
The sum of A equals 18 and the sum of B equals 12.
The difference d is -6 (sum_b - sum_a), it is even. Note that if difference is odd, then no swap is available and the result is False.
Then d is divided by 2. It becomes -3.
For A we get count [0, 1, 1, 1, 0, 0, 2, 0, 0, 0, 0] (as already mentioned before).
Then we just iterate though the list B using xrange and check the conditions (The loop goes from 0 and up to but not including 11). Let's check it step by step:
i = 0, B[0] - (-3) is 1 + 3 = 4. 4 is greater than or equal to 0 and less than or equal to 10 (remember, we have chosen m to be 10). However, count[4] is 0 and it's not greater than 0 (Note the list count starts from index 0). The condition fails, we go further.
i = 1, B[1] - (-3) is 5 + 3 = 8. 8 is greater than or equal to 0 and less than or equal to 10. However, count[8] is 0 and the condition fails.
i = 2, B[2] - (-3) is 3 + 3 = 6. 6 is greater than 0 and less than 10. Also count[6] is 2 and it is greater than 0. So we found the number. The loop stops, True is returned. It means that there is such a number in B which can be swapped with a number in A, so that sum of A becomes equal to the sum of B. Indeed, if we swap 6 in A with 3 in B, then their sum become equal to 15.
Hope this helps.
I'm not sure I get your idea correctly. Here's my understanding:
def counting(A, m):
n = len(A)
count = [0] * (m + 1)
for k in xrange(n):
count[A[k]] += 1
return count # this essentially build a counter
def fast_solution(A, B, m):
n = len(A)
sum_a = sum(A)
sum_b = sum(B)
d = sum_b - sum_a
if d % 2 == 1:
return False
d //= 2
count = counting(A, m) # get the dict
for i in xrange(n):
if 0 <= B[i] - d and B[i] - d <= m and count[B[i] - d] > 0:
# the first two conditions are to verify that B[i]-d exists as a key (index) in counter.
# then check if there actually exists the value.
# if count > 0, then you can swap the two to get same sum
return True
return False
Or rewriting to get:
def counting(A, m):
count = collections.Counter()
for i in A:
count[i] += 1
return count
def fast_solution(A, B, m):
n = len(A)
sum_a = sum(A)
sum_b = sum(B)
d = sum_b - sum_a
if d % 2 == 1:
return False
d //= 2
count = counting(A, m) # get the dict
for i in B:
if count[i-d]:
return True
return False
But in any case, this piece of code just check the solution existence with only single swap, be sure to check if that's what you wanted.

Sum of 2 elements from 2 ranges that will be one given number

I need to make a quick algorithm(I already made a slow one) which will find the number of all possible values from two ranges of integer numbers (ranges can intersect or not) which sum will be the given number
I can represent it like an equation: z = x + y
where z is a known number and equals x plus y
z can be any number between 0 and 10^18
x belongs to a range of integer numbers [a..b], where 0 <= a <= b <= 10^18 and
the difference between the consecutive numbers is 1
y belongs to a range of integer numbers [c..d], where 0 <= c <= d <= 10^18 and
the difference between the consecutive numbers is 1
so I need to find the number(not their exact values) of all the possible variations of x and y from two sets of numbers which sum will be z
Example:
z = 5
first set: a = 1, b = 5(it means the set consists of 1,2,3,4,5)
second set: c = 1, b = 5
then the answer is 4, because all possible combinations are:
x = 4, y = 1
x = 3, y = 2
x = 2, y = 3
x = 1, y = 4
because theirs sums are 5's
The compulsory condition for an algrorithm is to work faster than 1 second
The following code works fine but only with numbers lesser than 1000000. It starts to work much slower with big numbers
with open(r"input.txt") as f:
n = int(f.readline()) # the given number
a = int(f.readline()) # the start position of the first set
b = int(f.readline()) # the end position of the first set
c = int(f.readline()) # the start position of the second set
d = int(f.readline()) # the end position of the second set
# print "n:",n,"a:",a,"b:",b,"c:",c,"d:",d
t = b - a + 1 # all posible variants of the first set
k = d - c + 1 # all posible variants of the second set
number_of_vars = 0
if t >= k:
while b >= a:
if (n - b <= d) \
and (n - b>= c):
number_of_vars += 1
b -= 1
else:
b -= 1
if t < k:
while d >= c:
if (n-d <= b) and (n-d >= a):
number_of_vars += 1
d -= 1
else:
d -= 1
print number_of_vars
No algorithm required -- just algebra:
It suffices to count the number of x in [a,b] for which z - x is in [c,d]
You need both a <= x <= b and c <= z - x <= d. The second inequality is equivalent to z - d <= x <= z - c hence you need
max(a, z - d) <= x <= min(b,z - c)
The number of such x is 0 if min(b,z - c) < max(a, z - d) otherwise it is
min(b,z - c) - max(a, z - d) + 1
In either case the number of solutions is
max(0, min(b,z - c) - max(a, z - d) + 1)
In your example a = c = 1 and b = d = z = 5 and
min(b, z - c) - max(a, z - d) + 1 = min(5,4) - max(1,0) + 1 = 4 - 1 + 1 = 4
One thing that you can use to reduce the checks in your algorithm is,
If the range for the 2 sets are overlapping, then you can cancel out some checks. Like in your example,
range for 1st set is 1 to 5
range for 2nd set is 1 to 5
So, if
x = 4, y = 1
is working, then
x = 1, y = 4
will also work. So you have to go only till half the number (i.e till 3 only in this case)
If only a part of the range is overlapping, then you can use the above method for that part, and the remaining part can be checked using normal method.

Categories