web-dev-qa-db-fra.com

Tri rapide: itératif ou récursif

J'ai appris le tri rapide et comment il peut être implémenté dans les méthodes récursive et itérative.
En méthode itérative:

  1. Poussez la plage (0 ... n) dans la pile
  2. Partitionnez le tableau donné avec un pivot
  3. Pop l'élément supérieur.
  4. Poussez les partitions (plage d'index) sur une pile si la plage a plus d'un élément
  5. Effectuez les 3 étapes ci-dessus, jusqu'à ce que la pile soit vide

Et la version récursive est la version normale définie dans le wiki.

J'ai appris que les algorithmes récursifs sont toujours plus lents que leur homologue itératif.
Alors, quelle méthode est préférée en termes de complexité temporelle (la mémoire n'est pas un problème)?
Lequel est assez rapide à utiliser dans le concours de programmation?
Est-ce que c ++ STL sort () utilise une approche récursive?

20
sabari

En termes de complexité temporelle (asymptotique) - ils sont les mêmes.

"Récursif est plus lent qu'itératif" - le rationnel derrière cette déclaration est dû à la surcharge de la pile récursive (sauvegarde et restauration de l'environnement entre les appels).
Cependant, ce sont des nombres d'opérations constants, sans changer le nombre "d'itérations".

Le tri rapide récursif et itératif est O(nlogn) cas moyen et O(n^2) pire cas.


MODIFIER:

juste pour le plaisir, j'ai exécuté un test de référence avec le code (Java) attaché au message, puis j'ai exécuté test statistique wilcoxon , pour vérifier quelle est la probabilité que les temps d'exécution soient effectivement distincts

Les résultats sont concluants (P_VALUE = 2,6e-34, ce qui signifie que la probabilité qu'ils soient les mêmes est de 2,6 * 10 ^ -34 - très peu probable). Mais la réponse n'est pas celle que vous attendiez.
La moyenne de la solution itérative était de 408,86 ms tandis que celle de la récursivité était de 236,81 ms

(Remarque - J'ai utilisé Integer et non int comme argument pour recursiveQsort() - sinon le récursif aurait fait beaucoup mieux, car il ne doit pas contenir beaucoup de les entiers, ce qui prend aussi du temps - je l'ai fait parce que la solution itérative n'a pas d'autre choix que de le faire.

Ainsi - votre hypothèse n'est pas vraie, la solution récursive est plus rapide (pour ma machine et Java pour le moins) que l'itérative avec P_VALUE = 2.6e-34.

public static void recursiveQsort(int[] arr,Integer start, Integer end) { 
    if (end - start < 2) return; //stop clause
    int p = start + ((end-start)/2);
    p = partition(arr,p,start,end);
    recursiveQsort(arr, start, p);
    recursiveQsort(arr, p+1, end);

}

public static void iterativeQsort(int[] arr) { 
    Stack<Integer> stack = new Stack<Integer>();
    stack.Push(0);
    stack.Push(arr.length);
    while (!stack.isEmpty()) {
        int end = stack.pop();
        int start = stack.pop();
        if (end - start < 2) continue;
        int p = start + ((end-start)/2);
        p = partition(arr,p,start,end);

        stack.Push(p+1);
        stack.Push(end);

        stack.Push(start);
        stack.Push(p);

    }
}

private static int partition(int[] arr, int p, int start, int end) {
    int l = start;
    int h = end - 2;
    int piv = arr[p];
    swap(arr,p,end-1);

    while (l < h) {
        if (arr[l] < piv) {
            l++;
        } else if (arr[h] >= piv) { 
            h--;
        } else { 
            swap(arr,l,h);
        }
    }
    int idx = h;
    if (arr[h] < piv) idx++;
    swap(arr,end-1,idx);
    return idx;
}
private static void swap(int[] arr, int i, int j) { 
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

public static void main(String... args) throws Exception {
    Random r = new Random(1);
    int SIZE = 1000000;
    int N = 100;
    int[] arr = new int[SIZE];
    int[] millisRecursive = new int[N];
    int[] millisIterative = new int[N];
    for (int t = 0; t < N; t++) { 
        for (int i = 0; i < SIZE; i++) { 
            arr[i] = r.nextInt(SIZE);
        }
        int[] tempArr = Arrays.copyOf(arr, arr.length);

        long start = System.currentTimeMillis();
        iterativeQsort(tempArr);
        millisIterative[t] = (int)(System.currentTimeMillis()-start);

        tempArr = Arrays.copyOf(arr, arr.length);

        start = System.currentTimeMillis();
        recursvieQsort(tempArr,0,arr.length);
        millisRecursive[t] = (int)(System.currentTimeMillis()-start);
    }
    int sum = 0;
    for (int x : millisRecursive) {
        System.out.println(x);
        sum += x;
    }
    System.out.println("end of recursive. AVG = " + ((double)sum)/millisRecursive.length);
    sum = 0;
    for (int x : millisIterative) {
        System.out.println(x);
        sum += x;
    }
    System.out.println("end of iterative. AVG = " + ((double)sum)/millisIterative.length);
}
23
amit

La récursivité n'est PAS toujours plus lente que l'itération. Quicksort en est un parfait exemple. La seule façon de le faire de manière itérative est de créer une structure de pile. Donc, d'une autre manière, faites la même chose que le compilateur si nous utilisons la récursivité, et vous ferez probablement mieux que le compilateur. Il y aura également plus de sauts si vous n'utilisez pas la récursivité (pour faire sauter et pousser les valeurs pour les empiler).

9
Hauleth

C'est la solution que j'ai trouvée en Javascript. Je pense que ça marche.

function qs_iter(items) {
    if (!items || items.length <= 1) {
        return items
    }
    var stack = []
    var low = 0
    var high = items.length - 1
    stack.Push([low, high])
    while (stack.length) {
        var range = stack.pop()
        low = range[0]
        high = range[1]
        if (low < high) {
            var pivot = Math.floor((low + high) / 2)
            stack.Push([low, pivot])
            stack.Push([pivot+1, high])
            while (low < high) {
                while (low < pivot && items[low] <= items[pivot]) low++
                while (high > pivot && items[high] > items[pivot]) high--
                if (low < high) {
                    var tmp = items[low]
                    items[low] = items[high]
                    items[high] = tmp
                }
            }
        }
    }
    return items
}
</code>

Faites-moi savoir si vous avez trouvé une erreur :)

1
Nir M.