Bir Grid Python'da Desen Bulma [duplicate]
Rastgele 0 ve 1 içeren ızgara oluşturdum:
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
En büyük 1'ler kümesini , yani 4 öğeye eşit veya daha büyük (satır ve sütun boyunca) bulmak için ızgarada nasıl yineleme yapabilirim ?
Yineleme sırasında bulunan her bir kümenin sayısını tutmam ve 4'ten fazla öğelerini kaydetmem, bir listeye kaydetmem ve saymam ve ardından en büyük sayıyı bulmam gerektiğini varsayıyorum.
Sorun şu ki, bunu hem satırlar hem de sütunlar arasında nasıl yapacağımı ve sayımı kaydedemem. Izgara boyunca yineledim, ancak ikiden fazla satırın nasıl ilerleyeceğinden emin değilim.
Örneğin yukarıdaki örnekte, en büyük küme 8'dir. Izgarada başka kümeler de vardır, ancak bunların 4 öğesi vardır:
AA 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 BB 0 0 1 1
0 0 BB 1 1 1 0
0 1 0 0 1 0 1 1
Denediğim kod:
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
Yanıtlar
Üç algoritma uyguladım.
İlk algoritma, Simpleiç içe döngülerin en kolay yaklaşımını kullanarak, O(N^5) zaman karmaşıklığına sahiptir (burada bizim durumumuz için Ngiriş ızgarasının bir tarafıdır 10), çünkü boyut 10x10zaman girdilerimiz O(10^5)oldukça iyidir. Koddaki Algo kimliği algo = 0. Sadece bu algoritmayı görmek istiyorsanız, ------ Simple Algorithmkodun içindeki satıra atlayın .
İkinci algoritma, Dinamik Programlama yaklaşımını Advancedkullanarak , karmaşıklığı birinci algoritmadan çok daha hızlıdır. Koddaki Algo kimliği . Kod içindeki satıra atla .O(N^3)algo = 1------- Advanced Algorithm
Simple-ListCompSadece eğlenmek için uyguladığım üçüncü algoritma Simple, aynı O(N^5)karmaşıklık gibi , ancak normal döngüler yerine Python'un liste anlayışlarını kullanarak, bu yüzden daha kısa, ayrıca biraz daha yavaş çünkü bazı optimizasyonlar kullanmıyor. Koddaki Algo kimliği algo = 2. ------- Simple-ListComp AlgorithmAlgo'yu görmek için kodun içindeki satıra atlayın .
Kodun geri kalanı, algoritmaların yanı sıra, sonuçların doğruluğunu kontrol etmeyi (algoritmalar arasında çift kontrol), sonuçları yazdırmayı, metin girdileri üretmeyi uygular. Kod, görev çözme işlevi solve()ve test işlevi olarak ayrılmıştır test(). solve()işlev, işlevin davranışını yapılandırmaya izin veren birçok argümana sahiptir.
Tüm ana kod satırları yorumlarla belgelenir, kodu nasıl kullanacağınızı öğrenmek için bunları okuyun. Temel olarak, eğer sdeğişken, sorunuzda olduğu gibi, ızgara elemanlarına sahip çok satırlı metin içeriyorsa, sadece çalıştırırsınız solve(s, text = True)ve görevi çözer ve sonuçları yazdırır. Ayrıca, fonksiyonu çözmek için sonraki argümanları vererek algo = 0, check = False(burada algo 0 için 0) iki versiyondan (0 (Basit) ve 1 (Gelişmiş) ve 2 (Basit-ListeComp)) algoritmayı seçebilirsiniz . En test()basit kullanım örneğini görmek için işlev gövdesine bakın.
Varsayılan olarak konsola Algoritmalar çıkış tüm kümeler, büyük en küçüğüne, en büyük değeriyle .sembolü ile dinlenme B, C, D, ..., Zsemboller. show_non_max = FalseYalnızca ilk (en büyük) kümenin gösterilmesini istiyorsanız, çözme işlevinde bağımsız değişken ayarlayabilirsiniz .
Basit algoritmayı açıklayacağım:
- Temelde algoritmanın yaptığı şey - olası tüm açılı
1sdikdörtgenleri arar ve maksimum sayıları hakkındaki bilgilerima2D dizide depolar .Top-leftböyle bir dikdörtgenin noktası(i, j),top-right-(i, k),bottom-left-(l, j + angle_offset),bottom-right-(l, k + angle_offset), 4 köşenin tümü, bu yüzden bu kadar çok döngüye sahibiz. - In outer two
i(row) ,j(column) loops we iterate over whole grid, this(i, j)position will betop-leftpoint of1srectangle, we need to iterate whole grid because all possible1srectangles may havetop-leftat any(row, col)point of whole grid. At start ofjloop we check that grid at(i, j)position should always contain1because inside loops we search for all rectangle with1sonly. kloop iterates through all possibletop-rightpositions(i, k)of1srectangle. We should break out of loop if(i, k)equals to0because there is no point to extendkfurther to right because such rectangle will always contain0.- In previous loops we fixed
top-leftandtop-rightcorners 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 first0. offloop tries extending rectangle downwards at all possible angles (0(straight vertical),+1(45degrees shifted to the right from top to bottom),-1(-45degrees)),offbasically is such number thatgrid[y][x]is "above" (corresponds to byY)grid[y + 1][x + off].ltries to extend rectangle downwards (inYdirection) at different anglesoff. It is extended till first0because it can't be extended further then (because each such rectangle will already contain0).- Inside
lloop there isif grid[l][max(0, j + off * (l - i)) : min(k + 1 + off * (l - i), c)] != ones[:k - j + 1]:condition, basically thisifis meant to check that last row of rectangle contains all1if not thisifbreaks out of loop. This condition compares twolistslices for non-equality. Last row of rectangle spans from point(l, j + angle_offset)(expressionmax(0, j + off * (l - i)), max-limited to be0 <= X) to point(l, k + angle_offset)(expressionmin(k + 1 + off * (l - i), c), min-limited to beX < c). - Inside
lloop there are other lines,ry, rx = l, k + off * (l - i)computesbottom-rightpoint of rectangle(ry, rx)which is(l, k + angle_offset), this(ry, rx)position is used to store found maximum insidemaarray, this array stores all maximal found rectangles,ma[ry][rx]contains info about rectangle that hasbottom-rightat point(ry, rx). rv = (l + 1 - i, k + 1 - j, off)line computes new possible candidate forma[ry][rx]array entry, possible becausema[ry][rx]is updated only if new candidate has larger area of1s. Hererv[0]value insidervtuple containsheightof such rectangle,rv[1]containswidthof such rectangle (widthequals 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 ifrvarea is larger than current maximum inside arrayma[ry][rx]and if it is larger then this array entry is updated (ma[ry][rx] = rv). I'll remind thatma[ry][rx]contains info(width, height, angle)about current found maximal-area rectangle that hasbottom-rightpoint at(ry, rx)and that has thesewidth,heightandangle. - Done! After algorithm run array
macontains information about all maximal-area angled rectangles (clusters) of1sso that all clusters can be restored and printed later to console. Largest of all such1s-clusters is equal to somerv0 = ma[ry0][rx0], just iterate once through all elements ofmaand find such point(ry0, rx0)so thatma[ry0][rx0][0] * ma[ry0][rx0][1](area) is maximal. Then largest cluster will havebottom-rightpoint(ry0, rx0),bottom-leftpoint(ry0, rx0 - rv0[1] + 1),top-rightpoint(ry0 - rv0[0] + 1, rx0 - rv0[2] * (rv0[0] - 1)),top-leftpoint(ry0 - rv0[0] + 1, rx0 - rv0[1] + 1 - rv0[2] * (rv0[0] - 1))(hererv0[2] * (rv0[0] - 1)is just angle offset, i.e. how much shifted is first row alongXcompared 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
----------------------------