0
votes

Améliorer l'efficacité de Alog (faire pivoter le tableau vers la gauche de n itérations)

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


1 commentaires

Inversez l'ensemble du tableau (en place). Puis inversez entre 0 et iterations-1 . Puis inversez entre les itérations et a.length-1 . (En fait, c'est un changement à droite, mais la même idée).


5 Réponses :


1
votes

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)


5 commentaires

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.



-2
votes

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/


0 commentaires

0
votes

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


2 commentaires

Cela ne remplit pas tout cas d'utilisation, mais si peu seulement.


@AliAkram quel cas d'utilisation ne répondrait-il pas?



1
votes

Il y a quelques failles dans le code.

  • Tout d'abord, vous effectuez un travail redondant - si itérations> a.length - puis après a.length itérations, le tableau revient simplement à lui-même.
  • Deuxièmement, chaque itération crée une toute nouvelle copie du tableau!
  • Troisièmement, le nouvel emplacement de chaque élément du tableau peut être prédéterminé en ne regardant que la longueur du tableau, le nombre d'itérations requises et l'index de cet élément, pas besoin de répéter les itérations.

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.


3 commentaires

@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.



1
votes

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


6 commentaires

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 ().