Supprimer la couture de somme minimale d'une matrice


18

L'algorithme de sculpture de couture, ou une version plus complexe de celui-ci, est utilisé pour le redimensionnement d'image sensible au contenu dans divers programmes graphiques et bibliothèques. Jouons au golf!

Votre entrée sera un tableau d'entiers rectangulaire à deux dimensions.

Votre sortie sera le même tableau, une colonne plus étroite, avec une entrée supprimée de chaque ligne, ces entrées représentant un chemin de haut en bas avec la somme la plus faible de tous ces chemins.

Illustration de sculpture de couture https://en.wikipedia.org/wiki/Seam_carving

Dans l'illustration ci-dessus, la valeur de chaque cellule est indiquée en rouge. Les nombres noirs sont la somme de la valeur d'une cellule et le plus petit nombre noir dans l'une des trois cellules au-dessus (indiqué par les flèches vertes). Les chemins surlignés en blanc sont les deux chemins de somme les plus faibles, tous deux avec une somme de 5 (1 + 2 + 2 et 2 + 2 + 1).

Dans le cas où il y a deux chemins liés pour la somme la plus basse, peu importe celui que vous supprimez.

L'entrée doit provenir de stdin ou d'un paramètre de fonction. Il peut être formaté d'une manière appropriée à la langue de votre choix, y compris les crochets et / ou les délimiteurs. Veuillez préciser dans votre réponse comment la saisie est attendue.

La sortie doit être stdout dans un format délimité sans ambiguïté, ou comme une valeur de retour de fonction dans l'équivalent de votre langue à un tableau 2D (qui peut inclure des listes imbriquées, etc.).

Exemples:

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: Les nombres seront tous non négatifs, et chaque couture possible aura une somme qui tient dans un entier 32 bits signé.


Dans les exemples, toutes les valeurs de cellule sont des nombres à un chiffre. Est-ce garanti? Dans la négative, existe-t-il d'autres hypothèses concernant la taille / plage des valeurs? Par exemple, que la somme rentre dans une valeur 16/32 bits? Ou du moins que toutes les valeurs sont positives?
Reto Koradi

@RetoKoradi modifié avec des détails sur la portée
Sparr

Réponses:


5

CJam, 51 44 octets

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

Il s'agit d'une fonction anonyme qui extrait un tableau 2D de la pile et en envoie un en retour.

Essayez les cas de test en ligne dans l' interpréteur CJam . 1

Idée

Cette approche parcourt toutes les combinaisons possibles d'éléments de ligne, filtre celles qui ne correspondent pas aux coutures, trie par la somme correspondante, sélectionne le minimum et supprime les éléments correspondants du tableau. 2

Code

_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 Notez que CJam ne peut pas distinguer les tableaux vides des chaînes vides, car les chaînes ne sont que des tableaux dont les éléments sont des caractères. Ainsi, la représentation sous forme de chaîne des tableaux vides et des chaînes vides est "".

2 Alors que la complexité temporelle de l'algorithme présenté sur la page Wikipedia doit être de O (nm) pour une matrice n × m , celle-ci est au moins de O (m n ) .


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

Malheureusement, cela ne fonctionnera pas pour le deuxième cas de test. J'ai déposé un rapport de bug à ce sujet il y a deux semaines.
Dennis

5

Haskell, 187 octets

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

Exemple d'utilisation:

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

Fonctionnement, version courte: construire une liste de tous les chemins (1), par chemin: supprimer les éléments correspondants (2) et additionner tous les éléments restants (3). Prenez le rectangle avec la plus grande somme (4).

Version plus longue:

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 octets

Meh, je suis sûr que cela ne gagnera pas car c'est long, mais voici une solution simple:

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

Non 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

Nous créons de manière itérative le réseau d'énergie et suivons dans quelle direction va la couture, puis construisons une liste de suppression une fois que nous connaissons la position finale. Retirez la couture via l'indexation 1D, puis remettez-la dans le tableau avec les nouvelles dimensions.


3
Oh mon dieu ... Je pense que je viens de vomir un peu en voyant IDL (encore). Je pensais que j'avais fini de voir cela après l'obtention du diplôme ...
Kyle Kanos

Cela dit, je soupçonne que cela fonctionne également pour GDL, afin que les personnes qui ne souhaitent pas payer 1 milliard de dollars pour la licence mono-utilisateur puissent la tester?
Kyle Kanos

Je n'ai jamais utilisé GDL, donc je ne peux pas dire (honnêtement, j'ai oublié qu'il existait). La seule chose qui pourrait causer un problème est que GDL ne peut pas gérer la création de tableau de la syntaxe [0:n]; si c'est vrai, il est facile de le remplacer r+=[0:z[1]-1]*z[0]par r+=indgen(z[1]-1)*z[0].
sirpercival

De plus, bien que je préfère utiliser python pour mes golfs, personne d'autre ne fait IDL, je me sens donc obligé de contribuer XD. De plus, il fait très bien certaines choses.
sirpercival

3
Je me fais très grincer des dents / pleurer très bien;)
Kyle Kanos

3

JavaScript ( ES6 ) 197209 215

Implémentation étape par étape de l'algorithme wikipedia.

Peut probablement être raccourci davantage.

Testez l'exécution de l'extrait dans 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 octets

Cela ne gagnera aucun prix, mais je me suis amusé à y travailler. L'espace est réservé à des fins esthétiques et n'est pas inclus dans le nombre d'octets.

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

Ce code définit une fonction anonyme dont l'argument et la valeur de retour sont des listes imbriquées. Il implémente l'algorithme de la page Wikipedia: a(l'argument) est les nombres rouges et zles nombres noirs.

Voici une version avec harnais de test:

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)

Résultats:

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

Et voici l'équivalent approximatif de Python 3. Si quelqu'un veut une meilleure explication du code Pip, il suffit de demander dans les commentaires.

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.