Retire la costura de suma mínima de una matriz


18

El algoritmo de tallado de costura, o una versión más compleja del mismo, se utiliza para cambiar el tamaño de la imagen con contenido en varios programas gráficos y bibliotecas. ¡Vamos a jugar golf!

Su entrada será una matriz rectangular de dos números enteros.

Su salida será la misma matriz, una columna más estrecha, con una entrada eliminada de cada fila, esas entradas representan una ruta de arriba a abajo con la suma más baja de todas esas rutas.

Ilustración de talla de costura https://en.wikipedia.org/wiki/Seam_carving

En la ilustración anterior, el valor de cada celda se muestra en rojo. Los números negros son la suma del valor de una celda y el número negro más bajo en una de las tres celdas encima (señalado por las flechas verdes). Las rutas resaltadas en blanco son las dos rutas de suma más baja, ambas con una suma de 5 (1 + 2 + 2 y 2 + 2 + 1).

En un caso donde hay dos caminos atados para la suma más baja, no importa cuál elimines.

La entrada debe tomarse de stdin o como un parámetro de función. Se puede formatear de una manera conveniente para el idioma de su elección, incluidos los corchetes y / o delimitadores. Especifique en su respuesta cómo se espera la entrada.

La salida debe ser stdout en un formato delimitado sin ambigüedades, o como un valor de retorno de función en el equivalente de su idioma a una matriz 2d (que puede incluir listas anidadas, etc.).

Ejemplos:

Input:
1 4 3 5 2
3 2 5 2 3
5 2 4 2 1
Output:
4 3 5 2      1 4 3 5
3 5 2 3  or  3 2 5 3
5 4 2 1      5 2 4 2

Input:
1 2 3 4 5
Output:
2 3 4 5

Input:
1
2
3
Output:
(empty, null, a sentinel non-array value, a 0x3 array, or similar)

EDITAR: todos los números no serán negativos, y cada costura posible tendrá una suma que se ajuste a un entero de 32 bits con signo.


En los ejemplos, todos los valores de celda son números de un solo dígito. ¿Eso está garantizado? Si no, ¿hay otros supuestos que se pueden hacer sobre el tamaño / rango de los valores? Por ejemplo, que la suma cabe en un valor de 16/32 bits? ¿O al menos que todos los valores son positivos?
Reto Koradi

@RetoKoradi editado con detalles sobre el rango
Sparr

Respuestas:


5

CJam, 51 44 bytes

{_z,1$,m*{_1>.-W<2f/0-!},{1$.=:+}$0=.{WtW-}}

Esta es una función anónima que saca una matriz 2D de la pila y empuja una a cambio.

Pruebe los casos de prueba en línea en el intérprete de CJam . 1

Idea

Este enfoque itera sobre todas las combinaciones posibles de elementos de fila, filtra aquellas que no corresponden a costuras, ordena por la suma correspondiente, selecciona el mínimo y elimina los elementos correspondientes de la matriz. 2

Código

_z,   e# Get the length of the transposed array. Pushes the number of columns (m).
1$,   e# Get the length of the array itself. Pushes the number of rows (n).
m*    e# Cartesian power. Pushes the array of all n-tuples with elements in [0 ... m-1].
{     e# Filter:
  _1> e#     Push a copy of the tuple with first element removed.
  .-  e#     Vectorized difference.
  W<  e#     Discard last element.
  2f/ e#     Divide all by 2.
  0-  e#     Remove 0 from the results.
  !   e#     Push 1 if the remainder is empty and 0 otherwise.
},    e#     Keep only tuples which pushed a 1.

      e# The filtered array now contains only tuples that encode valid paths of indexes.

{     e# Sort by:
  1$  e#     Copy the input array.
  .=  e#     Retrieve the element of each row that corresponds to the index in the tuple.
  :+  e#     Add all elements.
}$    e#
0=    e# Retrieve the tuple of indexes with minimum sum.
.{    e# For each row in the array and the corresponding index in the tuple:
  Wt  e#     Replace the element at that index with -1.
  W-  e#     Remove -1 from the row.
}

1 Tenga en cuenta que CJam no puede distinguir entre matrices vacías y cadenas vacías, ya que las cadenas son solo matrices cuyos elementos son caracteres. Por lo tanto, la representación de cadena de las matrices vacías y las cadenas vacías es "".

2 Si bien la complejidad temporal del algoritmo que se muestra en la página de Wikipedia debe ser de O (nm) para una matriz n × m , esta es al menos de O (m n ) .


{2ew::m2f/0-!},
Optimizador

Lamentablemente, eso no funcionará para el segundo caso de prueba. He presentado un informe de error sobre esto hace dos semanas.
Dennis

5

Haskell, 187 bytes

l=length
f a@(b:c)=snd$maximum$(zip=<<map(sum.concat))$map(zipWith((uncurry((.drop 1).(++)).).flip splitAt)a)$iterate((\e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e])=<<)[[y]|y<-[0..l b-1]]!!l c

Ejemplo de uso:

*Main> f [[1,4,3,5,2],[3,2,5,2,3],[5,2,4,2,1]]
[[4,3,5,2],[3,5,2,3],[5,4,2,1]]

*Main> f [[1],[2],[3]]
[[],[],[]]

*Main> f [[1,2,3,4,5]]
[[2,3,4,5]]

Cómo funciona, versión corta: construya una lista de todas las rutas (1), por ruta: elimine los elementos correspondientes (2) y sume todos los elementos restantes (3). Tome el rectángulo con la suma más grande (4).

Versión más larga:

Input parameters, assigned via pattern matching:
a = whole input, e.g. [[1,2,4],[2,5,6],[3,1,6]]
b = first line, e.g. [1,2,4]
c = all lines, except first, e.g. [[2,5,6],[3,1,6]]

Step (1), build all paths:

iterate((\e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e])=<<)[[y]|y<-[0..l b-1]]!!l c

     [[y]|y<-[0..l b-1]]           # build a list of single element lists
                                   # for all numbers from 0 to length b - 1
                                   # e.g. [[0],[1],[2]] for a 3 column input.
                                   # These are all possible start points

     \e@(f:_)->[f-1:e,f:e,min(f+1)(l b-1):e]
                                   # expand a list of paths by replacing each
                                   # path with 3 new paths (up-left, up, up-right)

     (...)=<<                      # flatten the list of 3-new-path lists into
                                   # a single list

     iterate (...) [...] !! l c    # repeatedly apply the expand function to
                                   # the start list, all in all (length c) times.


Step (2), remove elements

map(zipWith((uncurry((.drop 1).(++)).).flip splitAt)a)

     (uncurry((.drop 1).(++)).).flip splitAt
                                   # point-free version of a function that removes
                                   # an element at index i from a list by
                                   # splitting it at index i, and joining the
                                   # first part with the tail of the second part

      map (zipWith (...) a) $ ...  # per path: zip the input list and the path with
                                   # the remove-at-index function. Now we have a list
                                   # of rectangles, each with a path removed

Step (3), sum remaining elements

zip=<<map(sum.concat)             # per rectangle: build a pair (s, rectangle)
                                  # where s is the sum of all elements


Step (4), take maximum

snd$maximum                      # find maximum and remove the sum part from the
                                 # pair, again.

3

IDL 8.3, 307 bytes

Meh, estoy seguro de que esto no ganará porque es largo, pero aquí hay una solución sencilla:

pro s,a
z=size(a,/d)
if z[0]lt 2then return
e=a
d=a*0
u=max(a)+1
for i=0,z[1]-2 do begin
e[*,i+1]+=min([[u,e[0:-2,i]],[e[*,i]],[e[1:*,i],u]],l,d=2)
d[*,i]=l/z[0]-1
endfor
v=min(e[*,-1],l)
r=intarr(z[1])+l
for i=z[1]-2,0,-1 do r[0:i]+=d[r[i+1],i]
r+=[0:z[1]-1]*z[0]
remove,r,a
print,reform(a,z[0]-1,z[1])
end

Sin golf:

pro seam, array
  z=size(array, /dimensions)
  if z[0] lt 2 then return
  energy = array
  ind = array * 0
  null = max(array) + 1
  for i=0, z[1]-2 do begin
    energy[*, i+1] += min([[null, energy[0:-2,i]], [energy[*,i]], [energy[1:*,i], null]], loc ,dimension=2)
    ind[*, i] = loc / z[0] - 1
  endfor
  void = min(energy[*,-1], loc)
  rem = intarr(z[1]) + loc
  for i=z[1]-2, 0, -1 do rem[0:i] += ind[rem[i+1], i]
  rem += [0:z[1]-1]*z[0]
  remove, rem, array
  print, reform(array, z[0]-1, z[1])
end

Creamos iterativamente la matriz de energía y rastreamos en qué dirección va la costura, luego construimos una lista de eliminación una vez que conocemos la posición final. Retire la costura a través de la indexación 1D, luego vuelva a reformar en la matriz con las nuevas dimensiones.


3
Oh dios ... creo que acabo de vomitar un poco viendo IDL (otra vez). Pensé que había terminado de ver eso después de la graduación ...
Kyle Kanos

Dicho esto, sospecho que esto también funciona para GDL, por lo que las personas que no están dispuestas a pagar $ 1 mil millones por la licencia de usuario único pueden probarlo.
Kyle Kanos

Nunca he usado GDL, así que no puedo decir (honestamente, olvidé que existía). Lo único que puede causar un problema es si GDL no puede manejar la creación de matriz de la sintaxis [0:n]; si eso es cierto, entonces es fácil de reemplazar r+=[0:z[1]-1]*z[0]con r+=indgen(z[1]-1)*z[0].
Sirpercival

Además, aunque preferiría usar Python para mis campos de golf, nadie más hace IDL, así que me siento obligado a contribuir con XD. Además, hace algunas cosas muy bien.
Sirpercival

3
Me hace encogerme / llorar muy bien;)
Kyle Kanos

3

JavaScript ( ES6 ) 197 209 215

Implementación paso a paso del algoritmo wikipedia.

Probablemente se puede acortar más.

Prueba a ejecutar el fragmento en Firefox.

// Golfed

F=a=>(u=>{for(r=[i=p.indexOf(Math.min(...p))];l--;i=u[l][i])(r[l]=[...a[l]]).splice(i,1)})
(a.map(r=>[r.map((v,i)=>(q[i]=v+~~p[j=p[i+1]<p[j=p[i-1]<p[i]?i-1:i]?i+1:j],j),q=[++l]),p=q][0],p=[l=0]))||r

// LESS GOLFED

U=a=>{
  p = []; // prev row
  u = a.map( r => { // in u the elaboration result, row by row
      q=[];
      t = r.map((v,i) => { // build a row for u from a row in a
        j = p[i-1] < p[i] ? i-1 : i; // find position of min in previous row
        j = p[i+1] < p[j] ? i+1 : j;
        q[i] = v + ~~p[j]; // values for current row
        // ~~ convert to number, as at first row all element in p are 'undefined'
        return j;//  position in u, row by row
      });
      p = q; // current row becomes previous row 
      return t;
  });
  n = Math.min(...p) // minimum value in the last row
  i = p.indexOf(n); // position of minimum (first if there are more than one present)
  r = []; // result      
  // scan u bottom to up to find the element to remove in the output row
  for(j = u.length; j--;)
  {
    r[j] = a[j].slice(); // copy row to output
    r[j].splice(i,1); // remove element
    i = u[j][i]; // position for next row
  }
  return r;
}

// TEST        
out=x=>O.innerHTML += x + '\n';        

test=[
  [[1,4,3,5,2],[3,2,5,2,3],[5,2,4,2,1]],
  [[1,2,3,4,5]],
  [[1],[2],[3],[4]]
];  

test.forEach(t=>{
  out('Test data:\n' + t.map(v=>'['+v+']').join('\n'));
  r=F(t);
  out('Golfed version:\n' + r.map(v=>'['+v+']').join('\n'))      
  r=U(t);
  out('Ungolfed version:\n' + r.map(v=>'['+v+']').join('\n'))
})  
<pre id=O></pre>


1

Pip, 91 bytes

Esto no ganará ningún premio, pero me divertí trabajando en ello. El espacio en blanco es solo por razones estéticas y no está incluido en el recuento de bytes.

{
 p:{(zaj-1+,3RMv)}
 z:a
 w:,#(a0)
 Fi,#a
  Fjw
   Ii
    z@i@j+:MN(pi-1)
 s:z@i
 Ti<0{
  j:s@?MNs
  a@i@:wRMj
  s:(p--i)
 }
 a
}

Este código define una función anónima cuyo argumento y valor de retorno son listas anidadas. Implementa el algoritmo de la página de Wikipedia: a(el argumento) son los números rojos y zson los números negros.

Aquí hay una versión con arnés de prueba:

f:{p:{(zaj-1+,3RMv)}z:aw:,#(a0)Fi,#aFjwIiz@i@j+:MN(pi-1)s:z@iTi<0{j:s@?MNsa@i@:wRMjs:(p--i)}a}
d:[
 [[1 4 3 5 2]
  [3 2 5 2 3]
  [5 2 4 2 1]]
 [[1 2 3 4 5]]
 [[1]
  [2]
  [3]]
 ]
Fld
 P(fl)

Resultados:

C:\> pip.py minSumSeam.pip -p
[[4;3;5;2];[3;5;2;3];[5;4;2;1]]
[[2;3;4;5]]
[[];[];[]]

Y aquí está el equivalente aproximado en Python 3. Si alguien quiere una mejor explicación del código Pip, solo pregunte en los comentarios.

def f(a):
    z = [row.copy() for row in a]
    w = range(len(a[0]))

    for i in range(len(a)):
        for j in w:
            if i:
                z[i][j] += min(z[i-1][max(j-1,0):j+2])
    s = z[i]
    while i >= 0:
        j = s.index(min(s))
        del a[i][j]
        i -= 1
        s = z[i][max(j-1,0):j+2]
    return a
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.