Rimuovere la cucitura della somma di minino da un array


18

L'algoritmo di seam carving, o una versione più complessa di esso, viene utilizzato per il ridimensionamento delle immagini in base al contenuto in vari programmi e librerie grafiche. Giochiamo a golf!

Il tuo input sarà un array rettangolare bidimensionale di numeri interi.

L'output sarà lo stesso array, una colonna più stretta, con una voce rimossa da ogni riga, quelle voci che rappresentano un percorso dall'alto verso il basso con la somma più bassa di tutti questi percorsi.

Illustrazione di intaglio della cucitura https://en.wikipedia.org/wiki/Seam_carving

Nell'illustrazione sopra, il valore di ogni cella è mostrato in rosso. I numeri neri sono la somma del valore di una cella e il numero nero più basso in una delle tre celle sopra di essa (indicate dalle frecce verdi). I percorsi evidenziati bianchi sono i due percorsi di somma più bassi, entrambi con una somma di 5 (1 + 2 + 2 e 2 + 2 + 1).

Nel caso in cui ci siano due percorsi legati per la somma più bassa, non importa quale rimuovi.

L'input deve essere preso dallo stdin o come parametro di funzione. Può essere formattato in un modo conveniente per la tua lingua preferita, comprese parentesi e / o delimitatori. Si prega di specificare nella risposta come è previsto l'input.

L'output dovrebbe essere stdout in un formato delimitato in modo inequivocabile o come valore di ritorno di una funzione nella tua lingua equivalente a un array 2d (che potrebbe includere elenchi nidificati, ecc.).

Esempi:

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)

EDIT: I numeri saranno tutti non negativi e ogni possibile cucitura avrà una somma che si adatta a un intero con segno a 32 bit.


Negli esempi, tutti i valori di cella sono numeri a una cifra. È garantito? In caso contrario, ci sono altre ipotesi che possono essere fatte circa la dimensione / intervallo dei valori? Ad esempio, la somma si adatta a un valore di 16/32 bit? O almeno che tutti i valori sono positivi?
Reto Koradi,

@RetoKoradi modificato con dettagli sulla distanza
Sparr,

Risposte:


5

CJam, 51 44 byte

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

Questa è una funzione anonima che estrae un array 2D dallo stack e ne invia uno in cambio.

Prova i casi di test online nell'interprete CJam . 1

Idea

Questo approccio scorre su tutte le possibili combinazioni di elementi di riga, filtra quelli che non corrispondono alle giunture, ordina per somma corrispondente, seleziona il minimo e rimuove gli elementi corrispondenti dall'array. 2

Codice

_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 Notare che CJam non è in grado di distinguere tra array vuoti e stringhe vuote, poiché le stringhe sono solo array i cui elementi sono caratteri. Pertanto, la rappresentazione di stringa di entrambe le matrici vuote e le stringhe vuote è "".

2 Mentre la complessità temporale dell'algoritmo mostrato nella pagina Wikipedia dovrebbe essere di O (nm) per una matrice n × m , questa è almeno di O (m n ) .


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

Purtroppo, non funzionerà per il secondo caso di test. Ho presentato una segnalazione di bug su questo due settimane fa.
Dennis,

5

Haskell, 187 byte

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

Esempio di utilizzo:

*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]]

Come funziona, versione corta: crea un elenco di tutti i percorsi (1), per percorso: rimuovi gli elementi corrispondenti (2) e somma tutti gli elementi rimanenti (3). Prendi il rettangolo con la somma più grande (4).

Versione più lunga:

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 byte

Meh, sono sicuro che questo non vincerà perché è lungo, ma ecco una soluzione semplice:

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

Ungolfed:

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

Creiamo iterativamente l'array di energia e tracciamo la direzione in cui va la cucitura, quindi costruiamo un elenco di rimozione una volta che conosciamo la posizione finale. Rimuovere la giuntura tramite l'indicizzazione 1D, quindi riformare nuovamente l'array con le nuove dimensioni.


3
Oddio ... Penso di aver vomitato un po 'vedendo IDL (di nuovo). Pensavo di aver finito di vederlo dopo la laurea ...
Kyle Kanos,

Detto questo, sospetto che questo funzioni anche per GDL, in modo che le persone non disposte a pagare $ 1 miliardo per la licenza per utente singolo possano testarlo?
Kyle Kanos,

Non ho mai usato GDL, quindi non posso dire (onestamente ho dimenticato che esistesse). L'unica cosa che potrebbe causare un problema è se GDL non è in grado di gestire la creazione di array della sintassi [0:n]; se è vero, è facile sostituirlo r+=[0:z[1]-1]*z[0]con r+=indgen(z[1]-1)*z[0].
sirpercival

Inoltre, mentre preferirei usare Python per i miei campi da golf, nessun altro fa IDL, quindi mi sento obbligato a contribuire con XD. Inoltre, fa alcune cose molto bene.
sirpercival

3
Mi fa rabbrividire / piangere molto bene;)
Kyle Kanos,

3

JavaScript ( ES6 ) 197 209 215

Implementazione passo-passo dell'algoritmo di Wikipedia.

Probabilmente può essere abbreviato di più.

Prova a eseguire lo snippet in 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 byte

Questo non vincerà alcun premio, ma mi sono divertito a lavorarci su. Lo spazio bianco è solo per motivi estetici e non è incluso nel conteggio dei byte.

{
 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
}

Questo codice definisce una funzione anonima il cui argomento e il valore restituito sono elenchi nidificati. Implementa l'algoritmo dalla pagina di Wikipedia: a(l'argomento) sono i numeri rossi, e zsono i numeri neri.

Ecco una versione con cablaggio di prova:

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)

risultati:

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

Ed ecco l'equivalente approssimativo in Python 3. Se qualcuno vuole una spiegazione migliore del codice Pip, basta chiedere nei commenti.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.