Codegolf — Aocg2021. День 13. Дефрагментация В Действии!

  • Автор темы Lionessa
  • Обновлено
  • 23, Oct 2024
  • #1

Часть Появление Кодекса Гольфа 2021 событие. Подробности смотрите в связанном мета-сообщении.

История продолжается с AoC2017, день 14.


Напомним: диск представляет собой прямоугольную сетку со строками \$r\$ и столбцами \$c\$. Каждая клетка на диске либо свободна (0), либо использована (1). На данный момент вы определили текущий статус диска (матрица 0-1) и количество регионов в нем (регион — это группа используемых квадратов, которые все являются смежными, не считая диагоналей).

Но мы еще не дефрагментировали диск! Поскольку мы определили области используемых квадратов, давайте предположим, что форма каждой области должна быть сохранена. Это затрудняет сжатие используемого пространства, но мы можем, по крайней мере, переместить каждый фрагмент влево. Давай сделаем это.

Более формально алгоритм будет выглядеть так:

  • Определите области используемых ячеек на диске.
  • Цикл до тех пор, пока нечего будет перемещать:
    • Выберите регион, который можно переместить на 1 единицу влево, не перекрывая его другим регионом.
    • Переместите его на 1 единицу влево. (Области не сливаются в одну, даже если после такого перемещения они становятся соседними.)

Вход: Прямоугольный массив нулей и единиц.

Выход: Прямоугольный массив одинакового размера, представляющий результат простой операции дефрагментации.

Например, если память выглядит так:(

 
 
 
 
 ...#####
...#...#
...#.#.#
.....#..
########
->
..#####.
..#...#.
..#..##.
.....#..
########

.....#####..
.....#...#..
.....#.#.#.#
.......#...#
.......#####
->
#####.......
#...#.......
##..##......
.#...#......
.#####......
 
is used, ####.... .####... ...##... #.####.. .###.... ##.##... .#.#.... #####... бесплатно)

0012.... .0123... ...45... 6.7448.. .774.... 77.49... .7.a.... 77baa...

тогда у него есть 12 отдельных регионов

00.1.2.. .0.1.2.3 ....4.5. 6.7.44.8 .77.4... 77..4..9 .7...a.. 77.b.aa.

который следует дефрагментировать следующим образом:

##.#.#.. .#.#.#.# ....#.#. #.#.##.# .##.#... ##..#..# .#...#.. ##.#.##.

что приводит к состоянию диска

.

Применяются стандартные правила. Выигрывает самый короткий код в байтах.

Дополнительные тестовые случаи

#

#код-гольф #код-гольф #массив #сетка #теория графов #бинарная матрица

Lionessa


Рег
23 Oct, 2004

Тем
68

Постов
205

Баллов
565
  • 26, Oct 2024
  • #2

Язык Wolfram (Математика), 109 байт

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 m->while(a=0*m;a[,1]=m[,1];while(a!=b=matrix(#a~,#a,i,j,m[i,j]&&matrix(#a~,#a,k,l,abs(i-k)+abs(j-l)<2&&a[k,l])),a=b);m!=n=a+concat((m-a)[,^1],0*m[,1]),m=n);m
 

Попробуйте онлайн!

Несколько раз выберите компоненты, ограничивающая рамка которых |p:&mut Vec<Vec<_>>|{fn k(x:usize,y:usize,p:&mut Vec<Vec<u8>>){if p[y][x]==1{p[y][x]+=2;if x>0{k(x-1,y,p)}if x<p[0].len()-1{k(x+1,y,p)}if y>0{k(x,y-1,p)}if y<p.len()-1{k(x,y+1,p)}}}loop{let q=p.clone();for j in 0..p.len(){k(0,j,p)}for i in 0..p[0].len(){for j in 0..p.len(){if p[j][i]==1{p[j].swap(i,i-1)}p[j][i]&=1}}if p==&q{break}}} is greater than 0, and move these components 1 unit to the left.

int M[][], // Integer-matrix on class-level, uninitialized v; // Value-integer on class-level, uninitialized m->{ // Method with integer-matrix parameter & boolean return int l=m[0].length, // The amount of columns in the matrix t=m.length*l, // The total amount of cells in the matrix i, // Index-integer, starting uninitialized F= // Flag-integer, whether we can still move anything v=1; // Set it and the class-level value both to 1 f, // Flag-integer, whether the current region can move x,a,b; // Temp-integers to save bytes, uninitialized for(M=m; // Set the class-level `M` to the input-matrix v++<t;) // Loop `v` in the range (1,t): for(i=t;i-->0;) // Inner loop over the cells: i=m[i/l][i%l]==1?// If the current cell contains a 1: f(i/l,i%l,v) // Flood-fill the region with the current `v` -1 // And break out of the inner loop : // Else: i; // Simply continue the loop // (now every region has its own positive integer) for(;F>0;) // Loop as long as `F` is still 1: for(F=v=0; // Reset `F` to 0 v++<t // Inner loop `v` in the range (0,t): ; // After every iteration: F=f>0? // If `f` is 1: 1 // Set `F` to 1 as well : // Else: F){ // Leave `F` the same for(f=-1, // Reset `f` to -1 i=t;i-->0;) // Inner loop over the cells: f&=m[a=i/l][b=i%l]==v // If the current cell contains value `v`, &b>0? // and this is NOT the first column: (x=m[a][b-1])<1 // If the cell at the left contains a 0 |x==v // Or the cell at the left contains value `v`, &b>1? // and this is also not the second column: 1 // Bitwise-AND `f` with 1 : // Else: 0 // Bitwise-AND `f` with 0 instead // (if the check is truthy: -1→1; 0→0; 1→1; // if the check is falsey: -1→0; 0→0; 1→0) : // Else: f; // Bitwise-AND `f` with itself to stay `f` for(;f>0& // If `f` is now 1, which means we can move the region // with value `v`: ++i<t;) // Do another inner loop over the cells: m[a=i/l][b=i%l]-= // Decrease the value in the current cell by: m[a][b]==v // If the current cell contains value `v`, &b>0? // and this is NOT the first column: m[a][b-1]=v // Set the cell at the left to `v` // Set the current cell to 0 by decreasing by `v` : // Else: 0; // Decrease by 0 to stay the same // After everything has been moved, for(;t-->0;) // loop one last time over the cells: m[t/l][t%l]= // Change the value in the current cell to: // Transform all positive values to 1, // while keeping any 0s the same: 1/~m[t/l][t%l]+1;// (1 integer-divided by (-value-1)) + 1 // Recursive method with coordinate as parameters & integer return-type int f(int x,int y){ try{if(M[x][y]==1){ // If the current cell contains a 1: M[x][y]=v; // Fill the cell with the current value f(m,x,y-1) // Recursive call west f(m,x-...,y) // Recursive call north f(m,x,y+...) // Recursive call east f(m,x+...,y);} // Recursive call south }finally{ // Catch and swallow any ArrayIndexOutOfBoundsExceptions // (shorter than manual if-checks) return 1;} // Always return 1, so we use it to our advantage to // save bytes function has a bug: when it takes an array instead of an image as input, the int M[][],v;m->{int l=m[0].length,t=m.length*l,i,F=v=1,f,x,a,b;for(M=m;v++<t;)for(i=t;i-->0;)i=m[i/l][i%l]==1?f(i/l,i%l)-1:i;for(;F>0;)for(F=v=0;v++<t;F=f>0?1:F){for(f=-1,i=t;i-->0;)f&=m[a=i/l][b=i%l]==v&b>0?(x=m[a][b-1])<1|x==v&b>1?1:0:f;for(;f>0&++i<t;)m[a=i/l][b=i%l]-=m[a][b]==v&b>0?m[a][b-1]=v:0;}for(;t-->0;)m[t/l][t%l]=1/~m[t/l][t%l]+1;}int f(int x,int y){try{if(M[x][y]==1){M[x][y]=v;f(x+f(x,y+f(x-f(x,y-1),y)),y);}}finally{return 1;}} вариант не работает. Итак, нам нужно преобразовать входные данные в изображение.

 

Shahan


Рег
09 Oct, 2011

Тем
78

Постов
197

Баллов
597
  • 26, Oct 2024
  • #4

JavaScript (Node.js), 190 байт

D

Попробуйте онлайн!

Введите как 1-мерный массив логических значений, а ширину как целое число. Выведите 1d массив логических значений.

 

Denis089


Рег
05 Feb, 2008

Тем
67

Постов
196

Баллов
541
  • 26, Oct 2024
  • #5

Древесный уголь, 120 105 байт

l

Попробуйте онлайн! Ссылка на подробную версию кода. Принимает входные данные в виде списка строк, заканчивающихся символом новой строки. C s and scipy.ndimage.label s и выводит список строк, разделенных символами новой строки. Объяснение:

from scipy.ndimage import* def f(A): B,J=label(A.T);j=1 while~J+j:C=B==j;B[C]=0;D=C^C;D[:-1]=C[1:];d=C[0].any()or B[D].any();B[[D,C][d]]=j;j=d*j+1 return B.T>0

Введите ячейки.

from scipy.ndimage import* def f(A): B,J=label(A.T);j=1 while~J+j:C=B==j;B[C]=0;D=C^C;D[:-1]=C[1:];d=C[0].any()|B[D].any();B[[D,C][d]]=j;j=d*j+1 return B.T>0

Получите смещение между каждой ячейкой и ячейкой ниже. (Это на единицу больше ширины, поэтому область в конце строки случайно не соединяется с областью в начале следующей строки.)

Eυэι§.#⊙η№ν⟦κμ

Получить список используемых ячеек.

FιFκUэλ⁻μν

Начните без регионов.

»WΦη⬤κ∧§μ¹¬⊙η›№ξEμ⁻ρς⁼ξκ

Повторяйте до тех пор, пока все используемые ячейки не будут распределены по региону.

⊞ηζ

Начните регион с последней (т. е. самой удачной) использованной ячейки.

F№θλ«⊞ζλ≔⁻θζθ»

Выполните поиск в ширину по региону.

FE⁴Eκ⁺ν∧⁼ξ&¹λ⊖&²λ

Перечислите координаты соседних ячеек.

Fζ

Если это используемая ячейка, то добавьте ее в регион и удалите из списка используемых ячеек.

≔⟦⊟θ⟧ζ

Сохраните этот регион в список регионов.

Wθ«

Повторяйте, пока любые регионы можно дефрагментировать...

»≔⟦⟧η

... переместите все ячейки в этих регионах на 1 ячейку влево. (Грустно ⊞υι doesn't do what I want here.)

F⌕Aι#⊞θ⟦Lυκ⟧

Выведите исходный ввод, обновленный новыми местоположениями всех используемых ячеек.

Предыдущая 120-байтовая версия не требовала прямоугольного ввода:

WS«

Попробуйте онлайн! Ссылка на подробную версию кода. Принимает входные данные в виде списка строк, заканчивающихся символом новой строки. ≔⟦⟧θ s and # s и выводит список строк, разделенных символами новой строки. Объяснение:

.

Начните с неиспользуемых ячеек.

≔⟦⟧θWS«F⌕Aι#⊞θ⟦Lυκ⟧⊞υι»≔⟦⟧ηWθ«≔⟦⊟θ⟧ζFζFE⁴Eκ⁺ν∧⁼ξ&¹λ⊖&²λF№θλ«⊞ζλ≔⁻θζθ»⊞ηζ»WΦη⬤κ∧§μ¹¬⊙η›№ξEμ⁻ρς⁼ξκFιFκUэλ⁻μνEυэι§.#⊙η№ν⟦κμ

Перебирайте входные строки.

Eυэι§.#⊙ζ№ν⁺×θκμ

Запишите использованные ячейки в эту строку.

MapAssign

Сохраните эту строку, чтобы выходные данные имели ту же форму, что и входные.

FιUэκ⊖λ

Начните без регионов.

»WΦζ⬤κ∧﹪μθ¬⊙ζ›№ξ⊖μ⁼ξκ

Повторяйте до тех пор, пока все используемые ячейки не будут распределены по региону.

⊞ζε

Начните регион с последней (т. е. самой удачной для гольфа) использованной ячейки.

F№ηλ«⊞ελ≔⁻ηεη»

Выполните поиск в ширину по региону.

F⁺κ⟦θ¹±¹±θ⟧

Перечислите координаты соседних ячеек.

Fε

Если это используемая ячейка, то добавьте ее в регион и удалите из списка используемых ячеек.

≔⟦⊟η⟧ε

Сохраните этот регион в список регионов.

Wη«

Повторяйте, пока любые регионы можно дефрагментировать...

≔⟦⟧ζ

... переместите все ячейки в этих регионах на 1 ячейку влево.

≔⌕A⪫υψ#η

Выведите исходный ввод, обновленный новыми местоположениями всех используемых ячеек.

 

Z0z0 - 1986


Рег
16 Jun, 2006

Тем
77

Постов
207

Баллов
622
  • 26, Oct 2024
  • #6

Питон 3 + сципи, 162, 160 байт

≔⊕Lθ

Попробуйте онлайн!

Старые версии:

WS⊞υι

Попробуйте онлайн!

Объяснение

Оставляет тяжелую работу (сегментацию) библиотечной функции. # .

Остальное относительно просто. Все делается с транспонированными осями, чтобы избежать беспорядка при индексации. Мы перемещаем один помеченный компонент на одну единицу за раз, пока не пройдем через все из них, не имея возможности больше ничего перемещать.

Более конкретно, . selects the pixels for label WS⊞υι≔⊕Lθθ≔⌕A⪫υψ#η≔⟦⟧ζWη«≔⟦⊟η⟧εFεF⁺κ⟦θ¹±¹±θ⟧F№ηλ«⊞ελ≔⁻ηεη»⊞ζε»WΦζ⬤κ∧﹪μθ¬⊙ζ›№ξ⊖μ⁼ξκFιUэκ⊖λEυэι§.#⊙ζ№ν⁺×θκμ и мы их тут же обнуляем; w=>g=a=>a.some((c,i)=>!a.some(n=_=>a.some((d,j)=>(r=~j%w?j+1:j,d-c)?d*n[r]:(n[j]=[j,j%w?j-1:j,r,j-w,j+w].some(k=>n[k]))*!(j%w)),n[i]=++t))?g(a.map((c,i)=>n[i+1]?t:!n[i]*c)):a.map(c=>c>0);t=1 shifts the selection. import re r=range def f(b,x,y,t): b[x][y]=t for j,k in[[1,0],[0,1],[-1,0],[0,-1]]: try: if x+j>=0 and y+k>=0 and b[x+j][y+k]==1:f(b,x+j,y+k,t) except:1 def g(b): n=map(chr,r(97,123)) while'1'in str(b):f(b,*[(x,y)for x in r(len(b))for y in r(len(b[x]))if b[x][y]==1][0],next(n)) return re.sub('\w','#',(q:=lambda r:r if r==(t:=re.sub('\.\w+',lambda x:j if re.findall('^'+(j:=x.group())[1]+'|(?<=[^'+'\.'+j[1]+'])'+j[1],r)else j[1:]+'.',r))else q(t))('\n'.join(''.join(['.',i][bool(i)]for i in k)for k in b))) принимает решение на основе того, есть ли помеченные пиксели (1) в базовом столбце или (2) (после смещения) столкновения с другими метками. Если // Converts a stringified number into a tuple type StrNumToTuple<T, N = []> = T extends `${N["length"]}` ? N : StrNumToTuple<T, [...N, 0]>; // Converts the input (e.g. [[0,0,1],[1,0,1],[0,1,1]]) // to a union of on cells ([2,0]|[0,1]|[2,1]|[1,2]|[2,2]) (where the numbers are represented by tuples) type ToUnion<T> = // Map over the 2d array { [I in keyof T]: { [J in keyof T[I]]: T[I][J] extends 1 // if the cell is on, add this tuple ? [StrNumToTuple<J>, StrNumToTuple<I>] : never // put the results into a union }[keyof T[I]] }[number] // Retrieves an arbitrary element of a union type PopUnion<T> = (T extends T ? (x: () => T) => 0 : 0) extends (x: infer U) => 0 ? U extends () => infer V ? V : 0 : 0; type Inc<T> = [...T, 0]; type Dec<T> = T extends [0, ...infer X] ? X : []; // Get the orthogonal neighbor positions of a position type Neighbors<T> = T extends [infer A, infer B] ? ( ([Dec<A>, B] | [Inc<A>, B] | [A, Dec<B>] | [A, Inc<B>]) ) : 0; type FindGroup<All, Group = PopUnion<All>, LastGroup = 0> = [Group] extends [LastGroup] // If the group didn't change last iteration, return it ? Group // Add to Group the Neighbors of Group that are in All : FindGroup<All, Group | Extract<Neighbors<Group>, All>, Group> // Converts a union of positions into a tuple of unions of positions, grouped by connectedness type Group<Ungrouped, Groups = []> = [Ungrouped] extends [never] // If there are no positions left in Ungrouped, return Groups ? Groups // Find a group, remove its elements from Ungrouped, and add it to Groups : Group<Exclude<Ungrouped, FindGroup<Ungrouped>>, [...Groups, FindGroup<Ungrouped>]> // Shift a group to the left. Returns {} (a top type of sorts) if it's at the leftmost border type ShiftLeft<Group> = Group[0] extends [0, ...0[]] // If all X coordinates are >= 1, map over the positions in Group ? Group extends Group // Decrement the X coordinate of this position ? [Dec<Group[0]>, Group[1]] // Unreachable : 0 // Otherwise, return {} : {} // The meat of the logic. Continually shifts groups left until it can't anymore type Defrag<Groups, PrevGroups = 0> = Groups extends PrevGroups // If Groups == PrevGroups, return Groups ? Groups // Otherwise, recurse: : Defrag< { // Map over Groups [K in keyof Groups]: // If (Groups[number] (all live positions) - Groups[K]) shares no elements with ShiftLeft<Groups<K>>, Extract<Exclude<Groups[number], Groups[K]>, ShiftLeft<Groups[K]>> extends 0 // Change this group to ShiftLeft<Groups[K]> ? ShiftLeft<Groups[K]> // Otherwise, leave it unchanged : Groups[K] }, Groups > type Main<Input, FinalLivePositions = Defrag<Group<ToUnion<Input>>>[number]> = { // Map over every row of Input [I in keyof Input]: Input[I] extends infer Row ? { // Map over every cell of Row [J in keyof Row]: // If this coordinate is in FinalLivePositions, set this cell to 1; otherwise, set it to 0 [StrNumToTuple<J>, StrNumToTuple<I>] extends FinalLivePositions ? 1 : 0 } : 0 } is set we write the erased pixels back and move to the next label, if //@ts-ignore type a<T,N=[]>=T extends`${N["length"]}`?N:a<T,d<N>>;type b<T>={[I in keyof T]:{[J in keyof T[I]]:T[I][J]extends 1?[a<J>,a<I>]:never}[keyof T[I]]}[number];type c<T>=(T extends T?(x:()=>T)=>0:0)extends(x:infer U)=>0?U extends()=>infer V?V:0:0;type d<T>=[...T,0];type e<T>=T extends[0,...infer X]?X:[];type f<T>=T extends[infer A,infer B]?[e<A>,B]|[d<A>,B]|[A,e<B>]|[A,d<B>]:0;type g<P,T=c<P>,U=0>=[T]extends[U]?T:g<P,T|Extract<f<T>,P>,T>;type h<P,G=[]>=[P]extends[never]?G:h<Exclude<P,g<P>>,[...G,g<P>]>;type i<T>=T[0]extends[0,...0[]]?T extends T?[e<T[0]>,T[1]]:0:{};type j<T,U=0>=T extends U?T:j<{[K in keyof T]:Extract<Exclude<T[number],T[K]>,i<T[K]>>extends 0?i<T[K]>:T[K]},T>;type M<T,U=j<h<b<T>>>[number]>={[I in keyof T]:T[I]extends infer X?{[J in keyof X]:[a<J>,a<I>]extends U?1:0}:0} не установлено, мы сдвигаем их и сбрасываем индекс метки.

 

Bssjportillo


Рег
21 Jan, 2007

Тем
68

Постов
214

Баллов
574
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно