Permutations déguisées

17

Etant donné un n vecteur de dimension v avec des entrées réelles, pour une permutation la plus proche p de (1,2,...,n) par rapport à la l1 -Distance.

Détails

  • S'il est plus commode, vous pouvez utiliser les permutations de (0,1,...,n1) à la place. S'il y a plusieurs permutations les plus proches, vous pouvez sortir n'importe laquelle ou alternativement toutes.
  • La distance l1 entre deux vecteurs u,v est définie comme
    d(u,v)=i|uivi|.
  • Si vous le souhaitez, vous pouvez supposer que l'entrée se compose uniquement d'entiers.

Exemples

[0.5  1] -> [1 2], [2 1]
c*[1 1 ... 1] -> any permutation
[1 4 2 6 2] -> [1 4 3 5 2], [1 4 2 5 3]
[1 3 5 4 1] -> [2 3 5 4 1], [1 3 5 4 2]
[7 7 3 2 5 6 4 2] -> [8 7 3 2 5 6 4 1], [8 7 3 1 5 6 4 2], [7 8 3 2 5 6 4 1], [7 8 3 1 5 6 4 2]
[-2 4 5 7 -1 9 3] -> [1 4 5 6 2 7 3], [2 4 5 6 1 7 3], [1 4 5 7 2 6 3], [2 4 5 7 1 6 3]
[0 4 2 10 -1 10 5] -> [1 4 2 6 3 7 5], [1 4 3 6 2 7 5], [2 4 3 6 1 7 5], [3 4 2 6 1 7 5], [1 4 2 7 3 6 5], [1 4 3 7 2 6 5], [2 4 3 7 1 6 5], [3 4 2 7 1 6 5]

Script octave pour générer plus d'exemples.

flawr
la source
Sommes-nous assurés que tous les éléments de vseront supérieurs à 0? Ou, du moins, non 0?
Shaggy
1
Non, les entrées de vpeuvent être des entiers. (Ajout de quelques exemples supplémentaires.)
flawr
S'ils peuvent être des nombres réels, alors [1.6 2]c'est un cas de test important (l'algorithme gourmand / le tri lexicographique donne la mauvaise réponse).
histocrate
2
Dupliquer déguisé? Je ne suis pas sûr qu'il devrait être fermé en tant que tel, car il n'est pas évident que c'est la même tâche (comme le prouve maintenant xnor).
Arnauld
1
(En fait, ce n'est pas la même tâche, mais toutes les solutions du défi lié sont des solutions de celle-ci.)
Arnauld

Réponses:

13

Python 2 , 60 octets

def f(l):z=zip(l,range(len(l)));print map(sorted(z).index,z)

Essayez-le en ligne!

Utilise l'indexation zéro.

Un algorithme rapide avec une idée simple. Si nous avons besoin au lieu de permuter la liste d'entrée pour le rendre aussi proche (1,2,...,n) que possible, nous devrions trier, comme le prouve ci - dessous. Puisque nous sommes à la place permutant (1,2,...,n) , on choisit la permutation que de commander la même manière que la liste d'entrée, comme dans mon défi Imiter un ordre (sauf l'entrée peut avoir des répétitions). (Edit: miles a souligné ce défi plus identique , où Dennis a la même réponse .)

Claim: A permutation of the list l that minimizes its distance to (1,2,...,n) is l sorted.

Proof: Consider some other permutation l of l. We'll prove it can't be better than l sorted.

Pick two indices i,j that l has out-of-order, that is where i<j but li>lj. We show that swapping them can't increase the distance to (1,2,...,n). We note that swap changes the contribution these two elements as follows:

|lii|+|ljj||lij|+|lji|.

Here's a neat way to show this can't be an increase. Consider two people walking on a number line, one going from li to i and the other from lj to j. The total distance they walk is the expression on the left. Since i<j but li>lj, they switch who is higher on the number line, which means they must cross at some point during their walks, call it p. But when they reach p, they could then swap their destinations and walk the same total distance. And then, it can't be worse for them to have walked to their swapped destinations from the start rather than using p as a waypoint, which gives the total distance on the right-hand side.

So, sorting two out-of-order elements in l makes its distance to (1,2,...,n) smaller or the same. Repeating this process will sort l eventually. So, l sorted is at least as good as l for any choice of l, which means it as optimal or tied for optimal.

Note that the only property of (1,2,...,n) that we used is that it's sorted, so the same algorithm would work to permute any given list to minimize its distance to any fixed list.

In the code, the only purpose of z=zip(l,range(len(l))) is to make the input elements distinct, that is to avoid ties, while keeping the same comparisons between unequal elements. If the input we guaranteed to have no repeats, we could remove this and just have lambda l:map(sorted(l).index,l).

xnor
la source
brilliant insight
Jonah
You've simplified this into finding the ordering.
miles
@miles That's pretty funny, I completely forgot about that challenge even though I wrote an answer, and Dennis has this exact Python answer which I helped golf.
xnor
That "visual proof" is neat. I got the same idea but had to lay out each case of that formula to prove it. As a side remark, a few alternatives of obtaining ranks in Python using third-party libraries are shown in this post.
Joel
5

05AB1E, 7 bytes

āœΣαO}н

Try it online!


Explanation

ā              # get the numbers 1 to len(input) + 1
 œ             # Permutations of this
  Σ  }         # Sort by ...
   α           # Absolute difference
    O          # Sum these
      н        # And get the first one 
               # implicitly print
Expired Data
la source
1
Everytime i'm amazed by this, what 05AB1E can't do ?
The random guy
5
@Therandomguy There aren't a lot of things which cannot be done in 05AB1E, but it's pretty bad at: regex-based challenges; matrix-based challenges (although this has been improved after some new builtins); lack of imaginary numbers; date/time related challenges; etc. However, although hard, it can still be done usually. To give two examples: The Work Day Countdown (go to next day, and get day of week are done manually); Quine outputs itself in binary (UTF-8 conversion is done manually).
Kevin Cruijssen
@Grimy should be fixed now :)
Expired Data
3

Perl 6, 44 bytes

{permutations(+$_).min((*[]Z-$_)>>.abs.sum)}

Try it online!

Anonymous codeblock that returns the first minimum permutation with 0 indexing.

Explanation:

{                                          }   # Anonymous code block
 permutations(+$_)                             # From the permutations with the same length
                  .min(                   )    # Find the minimum by
                                      .sum       # The sum of
                                >>.abs           # The absolute values of
                       (*[]Z-$_)                 # The zip subtraction with the input

I think I might also be able to get rid of the .sum and sort by just the list of absolute values, but I'm not sure this is actually corret, though it passes my current test cases.

Jo King
la source
1
That was breaking my brain too (or the mostly-equivalent question of "does a greedy algorithm work for this?"). The simplest counterexample is [0.6 1] (assuming we're 0-indexed), where if you optimize for the first value you get [1,0] for a score of 1.4, but if you optimize for the whole vector the 1 is more valuable in the second position for a score of 0.6.
histocrat
2

Jelly, 5 bytes

Œ¿œ?J

A monadic Link accepting a list of numbers which yields a list of integers.

Try it online! Or see the test-suite.

How?

Œ¿œ?J - Link: list of numbers, X
Œ¿    - Index of X in a lexicographically sorted list of
         all permutations of X's items
    J - range of length of X
  œ?  - Permutation at the index given on the left of the
         items given on the right

N.B. L (length of) would work in place of J since œ? given an integer, n, on the right would implicitly make the range [1..n] to work with, but J is explicit.

Jonathan Allan
la source
2

Ruby, 63 60 bytes

->v{[*1..v.size].permutation.max_by{|p|eval [p,0]*'*%p+'%v}}

Try it online!

There's a math trick here that could be helpful in other answers too--instead of minimizing the sum of the absolute values of the differences, we maximize the sum of the products. Why does that work?

Minimizing the sum of (x-y) squared isn't equivalent to minimizing the sum of |x-y|, but it will always give a valid answer, it just prioritizes reducing large differences over small ones whereas the actual challenge is indifferent between the two.

But (x-y)*(x-y) = x*x+y*y-2*x*y. Since the square terms will always show up somewhere in the sum for any permutation, they don't affect the result, so we can simplify to -2*x*y. The 2 factors out, so we can simplify to -x*y. Then if we change minimizing to maximizing, we can simplify to x*y.

Intuitively, this is similar to observing that if you're trying to maximize square footage using a set of horizontal walls and a set of vertical ones, you're best off pairing walls that are close in size to each other to create rooms that are as close to square as possible. 3*3 + 4*4 = 25, while 3*4 + 4*3 = 24.

Edit: Saved three bytes by generating and evaluating a format string instead of using zip and sum.

histocrat
la source
2
Minimizing the sum of (x-y) squared isn't equivalent to minimizing the sum of |x-y|, but it will always give a valid answer. Why is it the case? Is there no y that minimizes |xy| but not (xy)2?
Joel
1

Gaia, 13 bytes

e:l┅f⟪D†Σ⟫∫ₔ(

Try it online!

e:		| eval and dup input
l┅f		| push permutations of [1..length(input)]
⟪   ⟫∫ₔ		| iterate over the permutations, sorting with minimum first
 D†Σ		| the sum of the absolute difference of the paired elements
       (	| and select the first (minimum)
Giuseppe
la source
1

JavaScript (ES6), 61 bytes

Based on xnor's insight.

a=>[...a].map(g=n=>g[n]=a.sort((a,b)=>a-b).indexOf(n,g[n])+1)

Try it online!

Commented

a =>                    // a[] = input array
  [...a]                // create a copy of a[] (unsorted)
  .map(g = n =>         // let g be in a object; for each value n in the copy of a[]:
    g[n] =              //   update g[n]:
      a.sort(           //     sort a[] ...
        (a, b) => a - b //       ... in ascending order
      ).indexOf(        //     and find the position
        n,              //       of n in this sorted array,
        g[n]            //       starting at g[n] (interpreted as 0 if undefined)
      ) + 1             //     add 1
  )                     // end of map()

JavaScript (ES6),  130  128 bytes

There  must be  definitely is a more direct way...

0-indexed.

a=>(m=g=(k,p=[])=>1/a[k]?(h=i=>i>k||g(k+1,b=[...p],b.splice(i,0,k),h(-~i)))``:p.map((v,i)=>k+=(v-=a[i])*v)|k>m||(R=p,m=k))(0)&&R

Try it online! (with 1-indexed output)

How?

The helper function g computes all permutations of (0,...,n1), where n is the implicit length of the input array a[].

For each permutation p, we compute:

k=n1+i=0n1(piai)2
The only reason for the leading n1 is that we re-use the internal counter of g to save a few bytes, but it has no impact on the final result.

We eventually return the permutation that leads to the smallest k.

Arnauld
la source
1

Python 2, 149 126 112 bytes

-23 bytes thanks to Mr. Xcoder

-14 bytes thanks to xnor

from itertools import*
f=lambda a:min(permutations(range(len(a))),key=lambda x:sum(abs(a-b)for a,b in zip(x,a)))

Try it online!

Uses permutations of (0 ... n-1).

Hiatsu
la source
You can switch to Python 2, so that you don't need functools anymore.
Mr. Xcoder
reduce is usually overkill, especially here where you're adding stuff. I think you can just do sum(abs(p-q)for p,q in zip(x,a)).
xnor
0

w/o any permutation package

Python 3, 238 bytes

def p(a,r,l):
 if r==[]:l+=[a];return
 for i in range(len(r)):
  p(a+[r[i]],r[:i]+r[i+1:],l)
def m(l):
 s=(float("inf"),0);q=[];p([],list(range(len(l))),q)
 for t in q:D=sum(abs(e-f)for e,f in zip(l,t));s=(D,t)if D<s[0]else s
 return s[1]

Try it online!

david
la source
0

Japt -g, 12 bytes

Êõ á ñÈíaU x

Try it

For 0-indexed, replace the first 2 bytes with m, to map the array to its indices instead.

Êõ á ñÈíaU x     :Implicit input of array U
Ê                :Length
 õ               :Range [0,Ê]
   á             :Permutations
     ñÈ          :Sort by
       í U       :  Interleave with U
        a        :  Reduce each pair by absolute difference
           x     :  Reduce resulting array by addition
                 :Implicit output of first sub-array
Shaggy
la source
0

J, 25 8 bytes

#\/:@/:]

Try it online!

Much shorter answer based xnor's brilliant idea.

original answer

J, 25 bytes

(0{]/:1#.|@-"1)i.@!@#A.#\

Try it online!

Jonah
la source