Je résolvez un défi pour faire pivoter la matrice à gauche par N code> Nombre d'itérations.
Le code est à quasiment travaillé mais est en retard sur une très énorme contribution.
Comment améliorer l'efficacité
// Complete the rotLeft function below.
static int[] rotLeft(int[] a, int iterations) {
for(int i=0;i<iterations;i++)
{
int[] temp=Arrays.copyOfRange(a, 1, a.length);
temp=Arrays.copyOf(temp,a.length);
temp[a.length-1]=a[0];
a=temp;
}
return a;
}
5 Réponses :
Au lieu de déplacer les itérations
, vous pouvez calculer directement la position finale.
finalIndex=(index-iterations+a.length) % a.length
+ a.length est ajouté pour garantir que le code finalIndex > est toujours une valeur non négative.
Si vous appliquez ceci à votre algorithme, vous vous débarrassez de la boucle et faites le tout en une seule étape.
Cette complexité temporelle réduite de l'algorithme de O ( a.length * itérations) à O(a.length)
qu'est-ce que index
ici?
Il y a un moyen encore plus efficace :) (mais je n'ai pas le temps maintenant)
@Ali Akram: index
est la position de l'élément au début.
@josejuan: Plus rapide que O (a.length) ? Cela semble impossible, si nous avons besoin d'un tableau à la fin où tous les éléments ont changé.
@ MrSmith42 - pas plus rapide que O (a.length), mais temps constant plus rapide. Si vous faites pivoter un grand tableau avec une grande valeur d'itération, il y aura beaucoup d'erreurs de cache. Ma réponse montre une solution inplace (pas de deuxième tableau) qui effectue des échanges, ce qui est plus écrit, mais ceux-ci sont effectués dans des séquences séquentielles, quelles que soient les itérations, ce qui est plus convivial pour le cache.
Et à propos de ceci:
static int leftRotate(int arr[], int iterations, int k) { /* To get the starting point of rotated array */ int mod = k % iterations; // Prints the rotated array from // start position for(int i = 0; i < iterations; ++i) System.out.print(arr[(i + mod) % iterations] + " "); System.out.println(); } leftRotate(arr, iterations, arr.length);
Réf: https://www.geeksforgeeks.org/print-left-rotation-array/
Vous n'avez pas besoin de le faire à chaque itération. Au lieu de cela, vous pouvez créer une formule pour déterminer où chaque élément irait.
Par exemple, disons que la taille de votre tableau est n.
Ensuite, si vous devez déplacer votre tableau vers le gauche par 1 itération, alors un élément à la position p serait à (p + 1)% n position.
Pour i itérations, chaque élément serait à (p + i)% n emplacement. Ainsi, une boucle pour chaque itération n'est pas nécessaire.
static int[] rotLeft(int[] a, int iterations) { int[] answer = new int[a.length]; for(int i=0;i<a.length;i++) { answer[i] = a[(i - iterations + a.length) % (a.length)]; } return answer; }
Cela ne remplit pas tout cas d'utilisation, mais si peu seulement.
@AliAkram quel cas d'utilisation ne répondrait-il pas?
Il y a quelques failles dans le code.
itérations> a.length
- puis après a.length itérations, le tableau revient simplement à lui-même. En prenant cela en considération, cela peut se résumer à quelque chose sous la forme de:
static int[] rotLeftEfficient(int[] a, int iterations) { iterations = iterations % a.length; int[] b = new int[a.length]; for (int i = 0; i < a.length; i++) { int newIndex = (i - iterations + a.length) % a.length; b[newIndex] = a[i]; } return b; }
Cela se résume à O (n)
solution - où n
est le nombre d'éléments dans le tableau, avec des constantes décentes également.
@josejuan Pourquoi devrais-je penser autrement? Il n'est limité nulle part, et en tant qu'intervieweur, c'est un cas particulier que j'accepterais qu'un interviewé vérifie si j'utilise cette question dans une interview.
@josejuan je ne suis pas. Cette solution fonctionne pour toutes les entrées, même sans la condition préalable que itérations> a.length
avec un coût mineur (1 opération de module supplémentaire), je ne vois vraiment pas l'inconvénient de l'utiliser.
J'ai testé le code en utilisant un tableau d'éléments 2 ^ 28 = 268,435,456. Il a fallu près de 0,3 seconde pour allouer b [], donc je l'ai fait dans main () et j'ai passé b comme paramètre à la fonction rotate. Avec ce changement, la fonction de rotation prend environ 0,65 seconde, plus rapide que le pire des cas de ma réponse (rotation 1) 0,85 seconde, plus lente que les meilleurs cas de ma réponse 0,17 seconde. Je ne sais pas si Java optimise% a.length pour utiliser multiplier par inverse au lieu d'utiliser divide. Le% pourrait être remplacé par une séquence if / soustrait pour accélérer les choses. Le temps d'allocation pour un grand tableau de 0,3 seconde le rend globalement plus lent.
Voici une rotation sur place (n'utilise pas de deuxième tableau), avec une complexité temporelle O (a.length). Sur mon système (Intel 3770K 3,5 GHz), il peut faire pivoter un tableau d'éléments 2 ^ 28 = 268 435 456 en 0,17 (rotation 76) à 0,85 seconde (rotation 1). Les échanges sont effectués dans des séquences d'accès séquentielles, ce qui est compatible avec le cache.
public static void rol(int[] a, int r){ r = r % a.length; if(r == 0) return; int n = a.length - r; int i; int j; if(r <= n){ // if left rotate int[] b = new int[r]; // save elements for(j = 0; j < r; j++) b[j] = a[j]; for(i = 0; i < n; i++) // shift elements a[i] = a[j++]; for(j = 0; j < r; j++) // copy saved elements a[i++] = b[j]; } else { // else right rotate int[] b = new int[n]; // save elements i = 0; for(j = r; j < a.length; j++) b[i++] = a[j]; i = j-1; // shift elements for(j = i-n; j >= 0; j--) a[i--] = a[j]; for(j = 0; j < n; j++) // copy saved elements a[j] = b[j]; } }
Rotation simple à l'aide d'un deuxième tableau. Sur mon système (Intel 3770K 3,5 GHz), il peut faire pivoter un tableau de 2 ^ 28 = 268 435 456 éléments en 0,11 à 0,50 seconde.
// rotate in place public static void rolip(int[] a, int r){ r = r % a.length; if(r == 0) return; int t; int n = a.length - r; int i = 0; int j; while(true){ if(r <= n){ // shift left r places for(j = i+r; i < j; i++){ t = a[i]; // swap(a[i], a[i+r]) a[i] = a[i+r]; a[i+r] = t; } n -= r; if(n == 0) break; } else { // shift right n places i += r; for(j = i+n; i < j; i++){ t = a[i]; // swap(a[i], a[i-n]) a[i] = a[i-n]; a[i-n] = t; } i -= n+r; r -= n; if(r == 0) break; } } }
pour des valeurs min (r, a.length - r)
faibles, les performances sont très mauvaises, la pénalité est proportionnelle à 1 / min (r, a.length - r)
(donc, la pénalité peu importe les valeurs supérieures à 10 ~ 100 mais n'est pas acceptable pour les baisses)
@josejuan - ce type de fusion sur place est souvent utilisé pour les tris de fusion sur place (pas de second tableau), tels que Bloquer le tri par fusion .
"Il doit être très petit" 30% n'est pas très petit (même rolip
avec un r
différent), en fait, si vous utilisez un intermédiaire k = i + r; ... a [i] = a [k]; ... i ++, k ++
vous obtenez une (légère) meilleure performance. Vous pouvez probablement dérouler des boucles pour éviter cette surcharge (30%). Bref je pense que ta solution est la meilleure (le +1 est le mien;)
@josejuan - J'ai ajouté une simple rotation en utilisant un deuxième tableau (la taille est min (r, a.length-r)). Cela devrait être plus rapide que la rotation en place, dans la plupart des cas. Il y a une certaine surcharge pour allouer un deuxième grand tableau. J'obtiens également des conflits de cache avec certains modèles (puissance de taille de tableau de 2, rotation = (1/2 taille de tableau) + ou - 1.
Bonne approche mais, une fois que vous avez créé le tableau intermédiaire, pourquoi pas int i = a.length - (r% a.length); System.arraycopy (b, i, a, 0, a.length - i); System.arraycopy (b, 0, a, a.length - i, i);
?
@josejuan - La copie du tableau est-elle beaucoup plus rapide? Je pensais que le compilateur Java convertirait la boucle en arraycopy (), de la même manière que les compilateurs C / C ++ convertissent une boucle similaire en memcpy ().
Inversez l'ensemble du tableau (en place). Puis inversez entre
0
etiterations-1
. Puis inversez entre lesitérations
eta.length-1
. (En fait, c'est un changement à droite, mais la même idée).