Je travaille sur le tri d'une séquence entière sans nombre identique (sans perte de généralité, supposons que la séquence est une permutation de 1,2,...,n
) dans son ordre croissant naturel (c'est-à-dire 1,2,...,n
). Je pensais à échanger directement les éléments (indépendamment de la position des éléments; en d'autres termes, un échange est valable pour deux éléments) avec un nombre minimal d'échanges (ce qui suit peut être une solution réalisable):
Échangez deux éléments avec la contrainte que l'un ou les deux doivent être échangés dans la ou les positions correctes. Jusqu'à ce que chaque élément soit placé dans sa position correcte.
Mais je ne sais pas comment prouver mathématiquement si la solution ci-dessus est optimale. Tout le monde peut aider?
J'ai pu le prouver avec graph-theory . Pourrait vouloir ajouter cette balise :)
Créez un graphique avec n
sommets. Créer un Edge à partir du nœud n_i
à n_j
si l'élément en position i
doit être en position j
dans le bon ordre. Vous aurez maintenant un graphique composé de plusieurs cycles sans intersection. Je soutiens que le nombre minimum de swaps nécessaires pour commander correctement le graphique est
M = sum (c in cycles) size(c) - 1
Prenez une seconde pour vous en convaincre ... si deux articles sont dans un cycle, un échange peut juste prendre soin d'eux. Si trois éléments sont dans un cycle, vous pouvez échanger une paire pour en mettre un au bon endroit, et un cycle reste, etc. Si n
les éléments sont dans un cycle, vous avez besoin de n-1
swaps. (Cela est toujours vrai même si vous n'échangez pas avec des voisins immédiats.)
Cela étant, vous pouvez maintenant voir pourquoi votre algorithme est optimal. Si vous effectuez un échange et qu'au moins un élément est dans la bonne position, cela réduira toujours la valeur de M
de 1. Pour tout cycle de longueur n
, pensez à permuter un élément dans le bon endroit, occupé par son voisin. Vous avez maintenant un élément correctement ordonné et un cycle de longueur n-1
.
Puisque M
est le nombre minimum de swaps et que votre algorithme réduit toujours M
de 1 pour chaque swap, il doit être optimal.
Ehm, tout le comptage de cycles est très difficile à garder dans la tête. Il existe un moyen beaucoup plus simple à mémoriser.
Tout d'abord, allons jeter un exemple de cas manuellement.
swap(0,2);swap(0,3)
est identique à swap(2,3);swap(0,2)
swap(0, 1)
=> [(3, 2), (1, 1), (2, 3), (4, 4), (5 , 5), (6, 6), (0, 7)]swap(0, 3)
=> [(4, 4), (1, 1), (2, 3), (3, 2), (5 , 5), (6, 6), (0, 7)]swap(0, 4)
=> [(5, 5), (1, 1), (2, 3), (3, 2), (4, 4), (6, 6), (0, 7)]swap(0, 5)
=> [(6, 6), (1, 1), (2, 3), (3, 2), (4, 4), (5, 5), (0, 7)]swap(0, 6)
=> [(0, 7), (1, 1), (2, 3), (3, 2), (4, 4), (5, 5), (6, 6)]C'est à dire. sémantiquement, vous triez les éléments, puis découvrez comment les remettre à l'état initial en échangeant l'élément le plus à gauche qui n'est pas à sa place.
L'algorithme Python est aussi simple que cela:
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
def minimum_swaps(arr):
annotated = [*enumerate(arr)]
annotated.sort(key = lambda it: it[1])
count = 0
i = 0
while i < len(arr):
if annotated[i][0] == i:
i += 1
continue
swap(annotated, i, annotated[i][0])
count += 1
return count
Ainsi, vous n'avez pas besoin de mémoriser les nœuds visités ni de calculer une certaine longueur de cycle.
Pour votre référence, voici un algorithme que j'ai écrit, pour générer le nombre minimum de swaps nécessaires pour trier le tableau. Il trouve les cycles décrits par @Andrew Mao.
/**
* Finds the minimum number of swaps to sort given array in increasing order.
* @param ar array of <strong>non-negative distinct</strong> integers.
* input array will be overwritten during the call!
* @return min no of swaps
*/
public int findMinSwapsToSort(int[] ar) {
int n = ar.length;
Map<Integer, Integer> m = new HashMap<>();
for (int i = 0; i < n; i++) {
m.put(ar[i], i);
}
Arrays.sort(ar);
for (int i = 0; i < n; i++) {
ar[i] = m.get(ar[i]);
}
m = null;
int swaps = 0;
for (int i = 0; i < n; i++) {
int val = ar[i];
if (val < 0) continue;
while (val != i) {
int new_val = ar[val];
ar[val] = -1;
val = new_val;
swaps++;
}
ar[i] = -1;
}
return swaps;
}
Nous n'avons pas besoin d'échanger les éléments réels, il suffit de trouver combien d'éléments ne sont pas dans le bon index (Cycle). Les swaps min seront Cycle - 1; Voici le code ...
static int minimumSwaps(int[] arr) {
int swap=0;
boolean visited[]=new boolean[arr.length];
for(int i=0;i<arr.length;i++){
int j=i,cycle=0;
while(!visited[j]){
visited[j]=true;
j=arr[j]-1;
cycle++;
}
if(cycle!=0)
swap+=cycle-1;
}
return swap;
}
// En supposant que nous n'ayons affaire qu'à une séquence commencée par zéro
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start) {
j = arr[j]
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
@Archibald, j'aime votre solution, et telle était ma supposition initiale que trier le tableau serait la solution la plus simple, mais je ne vois pas la nécessité de passer par l'effort de la marche arrière comme je l'ai surnommé, c'est-à-dire énumérer puis trier le tableau puis calculer les échanges pour les énumérations.
Je trouve plus simple de soustraire 1 de chaque élément du tableau, puis de calculer les swaps nécessaires pour trier cette liste
voici mon Tweak/solution:
def swap(arr, i, j):
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
def minimum_swaps(arr):
a = [x - 1 for x in arr]
swaps = 0
i = 0
while i < len(a):
if a[i] == i:
i += 1
continue
swap(a, i, a[i])
swaps += 1
return swaps
Quant à prouver l'optimalité, je pense que @arax a un bon point.
Version Swift 4:
func minimumSwaps(arr: [Int]) -> Int {
struct Pair {
let index: Int
let value: Int
}
var positions = arr.enumerated().map { Pair(index: $0, value: $1) }
positions.sort { $0.value < $1.value }
var indexes = positions.map { $0.index }
var swaps = 0
for i in 0 ..< indexes.count {
var val = indexes[i]
if val < 0 {
continue // Already visited.
}
while val != i {
let new_val = indexes[val]
indexes[val] = -1
val = new_val
swaps += 1
}
indexes[i] = -1
}
return swaps
}
Si le compte du tableau commence par 1
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start + 1) {
j = arr[j] - 1
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
sinon pour entrée commençant par 0
function minimumSwaps(arr) {
var len = arr.length
var visitedarr = []
var i, start, j, swap = 0
for (i = 0; i < len; i++) {
if (!visitedarr[i]) {
start = j = i
var cycleNode = 1
while (arr[j] != start) {
j = arr[j]
visitedarr[j] = true
cycleNode++
}
swap += cycleNode - 1
}
}
return swap
}
Extension du code Darshan Puttaswamy pour les entrées HackerEarth actuelles
Solution bien faite par @bekce. Si vous utilisez C #, le code initial de configuration du tableau modifié ar
peut être exprimé succinctement comme suit:
var origIndexes = Enumerable.Range(0, n).ToArray();
Array.Sort(ar, origIndexes);
puis utilisez origIndexes
au lieu de ar
dans le reste du code.
Code Python
A = [4,3,2,1]
count = 0
for i in range (len(A)):
min_idx = i
for j in range (i+1,len(A)):
if A[min_idx] > A[j]:
min_idx = j
if min_idx > i:
A[i],A[min_idx] = A[min_idx],A[i]
count = count + 1
print "Swap required : %d" %count
Une implémentation sur des entiers avec des types primitifs dans Java (et tests).
import Java.util.Arrays;
public class MinSwaps {
public static int computate(int[] unordered) {
int size = unordered.length;
int[] ordered = order(unordered);
int[] realPositions = realPositions(ordered, unordered);
boolean[] touchs = new boolean[size];
Arrays.fill(touchs, false);
int i;
int landing;
int swaps = 0;
for(i = 0; i < size; i++) {
if(!touchs[i]) {
landing = realPositions[i];
while(!touchs[landing]) {
touchs[landing] = true;
landing = realPositions[landing];
if(!touchs[landing]) { swaps++; }
}
}
}
return swaps;
}
private static int[] realPositions(int[] ordered, int[] unordered) {
int i;
int[] positions = new int[unordered.length];
for(i = 0; i < unordered.length; i++) {
positions[i] = position(ordered, unordered[i]);
}
return positions;
}
private static int position(int[] ordered, int value) {
int i;
for(i = 0; i < ordered.length; i++) {
if(ordered[i] == value) {
return i;
}
}
return -1;
}
private static int[] order(int[] unordered) {
int[] ordered = unordered.clone();
Arrays.sort(ordered);
return ordered;
}
}
Les tests
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MinimumSwapsSpec {
@Test
public void example() {
// setup
int[] unordered = new int[] { 40, 23, 1, 7, 52, 31 };
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(5, minSwaps);
}
@Test
public void example2() {
// setup
int[] unordered = new int[] { 4, 3, 2, 1 };
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(2, minSwaps);
}
@Test
public void example3() {
// setup
int[] unordered = new int[] {1, 5, 4, 3, 2};
// run
int minSwaps = MinSwaps.computate(unordered);
// verify
assertEquals(2, minSwaps);
}
}
Swift 4.2:
func minimumSwaps(arr: [Int]) -> Int {
let sortedValueIdx = arr.sorted().enumerated()
.reduce(into: [Int: Int](), { $0[$1.element] = $1.offset })
var checked = Array(repeating: false, count: arr.count)
var swaps = 0
for idx in 0 ..< arr.count {
if checked[idx] { continue }
var edges = 1
var cursorIdx = idx
while true {
let cursorEl = arr[cursorIdx]
let targetIdx = sortedValueIdx[cursorEl]!
if targetIdx == idx {
break
} else {
cursorIdx = targetIdx
edges += 1
}
checked[targetIdx] = true
}
swaps += edges - 1
}
return swaps
}
Voici une solution en Java pour ce que @Archibald a déjà expliqué.
static int minimumSwaps(int[] arr){
int swaps = 0;
int[] arrCopy = arr.clone();
HashMap<Integer, Integer> originalPositionMap
= new HashMap<>();
for(int i = 0 ; i < arr.length ; i++){
originalPositionMap.put(arr[i], i);
}
Arrays.sort(arr);
for(int i = 0 ; i < arr.length ; i++){
while(arr[i] != arrCopy[i]){
//swap
int temp = arr[i];
arr[i] = arr[originalPositionMap.get(temp)];
arr[originalPositionMap.get(temp)] = temp;
swaps += 1;
}
}
return swaps;
}
Il s'agit de l'exemple de code en C++ qui trouve le nombre minimum de swaps pour trier une permutation de la séquence de (1,2,3,4,5,.......n-2,n-1,n)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i,j,k,num = 0;
cin >> n;
int arr[n+1];
for(i = 1;i <= n;++i)cin >> arr[i];
for(i = 1;i <= n;++i)
{
if(i != arr[i])// condition to check if an element is in a cycle r nt
{
j = arr[i];
arr[i] = 0;
while(j != 0)// Here i am traversing a cycle as mentioned in
{ // first answer
k = arr[j];
arr[j] = j;
j = k;
num++;// reducing cycle by one node each time
}
num--;
}
}
for(i = 1;i <= n;++i)cout << arr[i] << " ";cout << endl;
cout << num << endl;
return 0;
}