Faire pivoter un tableau entier avec un algorithme O (n) [fermé]


10

Écrivez une fonction qui fait tourner un tableau entier d'un nombre donné k. k éléments de la fin doivent se déplacer au début du tableau, et tous les autres éléments doivent se déplacer vers la droite pour créer l'espace.

La rotation doit être effectuée sur place.

L'algorithme ne doit pas fonctionner dans plus de O (n), où n est la taille du tableau.

Une mémoire constante doit également être utilisée pour effectuer l'opération.

Par exemple,

si le tableau est initialisé avec les éléments arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

rotation (arr, 3) fera que les éléments seront {7, 8, 9, 1, 2, 3, 4, 5, 6}

rotation (arr, 6) entraînera le {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Qu'entend-on ici par mémoire constante? Il faut sûrement au moins O (n) mémoire au minimum juste pour stocker la matrice en cours de traitement, rendant l' utilisation de la mémoire O (1) impossible.
Ad Hoc Garf Hunter

2
Je vote pour fermer cette question comme hors sujet parce que les questions sans critère de gain principal objectif sont hors sujet, car elles empêchent incontestablement de décider quelle entrée devrait gagner. Il n'y a absolument aucune raison que ce soit un concours de popularité.
James

A voté pour fermer. Du wiki du concours de popularité ( ici ), "Donne la liberté aux participants de décider quoi faire dans les parties cruciales et les encourage à utiliser cette liberté." Je ne pense pas que laisser le défi ouvert à n'importe quel algorithme compte pour encourager la créativité pour un défi aussi simple, du moins pas dans la mesure où il fonctionne comme un popcon. Ce serait plus adapté comme défi de code-golf .
mbomb007

Réponses:


18

C (104)

void reverse(int* a, int* b)
{
    while (--b > a) {
        *b ^= *a;
        *a ^= *b;
        *b ^= *a;
        ++a;
    }
}

void rotate(int *arr, int s_arr, int by)
{
    reverse(arr, arr+s_arr);
    reverse(arr, arr+by);
    reverse(arr+by, arr+s_arr);
}

Minifié:

v(int*a,int*b){while(--b>a){*b^=*a;*a^=*b;*b^=*a++;}}r(int*a,int s,int y){v(a,a+s);v(a,a+y);v(a+y,a+s);}

4
Vous devriez avoir écrit la condition de la boucle while commea <-- b
justhalf

Il fut un temps où les programmes C gagnaient des concours de popularité ...
Anubian Noob

Tu es le meilleur! Comment élégant et optimisé .. Pourriez-vous faire cela avec un tableau de bits?

9

APL (4)

¯A⌽B
  • A est le nombre d'endroits pour tourner
  • B est le nom du tableau à faire pivoter

Je ne sais pas si APL en a réellement besoin, mais dans l'implémentation que j'ai vue (les internes de), cela prendrait du temps proportionnel à A, et une mémoire constante.


+1 s'il s'agissait de golf :)
Glenn Teitelbaum

Il ne le fait pas en place cependant.
marinus

@marinus: C'est certainement le cas dans les implémentations que j'ai vues.
Jerry Coffin

Comment est-ce une fonction? Pourrait être {⍵⌽⍨-⍺}ou {⌽⍺⌽⌽⍵}. Dans NARS2000, il peut être élégamment écrit comme ⌽⍢⌽.
2015

5

Voici une version C de longue haleine de l'idée de Colin.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int gcd(int a, int b) {
  int t;
  if (a < b) {
    t = b; b = a; a = t;
  }
  while (b != 0) {
    t = a%b;
    a = b;
    b = t;
  }
  return a;
}

double arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int s_arr = sizeof(arr)/sizeof(double);

/* We assume 1 <= by < s_arr */
void rotate(double *arr, int s_arr, int by) {
  int i, j, f;
  int g = gcd(s_arr,by);
  int n = s_arr/g;
  double t_in, t_out;

  for (i=0; i<g; i++) {
    f = i;
    t_in = arr[f + s_arr - by];
    for (j=0; j<n; j++) {
      t_out = arr[f];
      arr[f] = t_in;
      f = (f + by) % s_arr;
      t_in = t_out;
    }
  }
}

void print_arr(double *arr, int s_arr) {
  int i;
  for (i=0; i<s_arr; i++) printf("%g ",arr[i]);
  puts("");
}

int main() {
  double *temp_arr = malloc(sizeof(arr));
  int i;

  for (i=1; i<s_arr; i++) {
    memcpy(temp_arr, arr, sizeof(arr));
    rotate(temp_arr, s_arr, i);
    print_arr(temp_arr, s_arr);
  }
}

Cela ne ressemble pas à une solution à mémoire constante, n'est-ce pas?
microbian

Oui, c'est une solution à mémoire constante. Le truc "mallocé" est une copie temporaire du tableau afin que je puisse y copier les données originales encore et encore, afin de pouvoir tester différentes quantités de rotation.
Stephen Montgomery-Smith

Qu'est-ce que la rotation réelle est la fonction "rotation". Il utilise 5 entiers et deux doubles. Il appelle également une fonction "gcd" qui utilise un entier et utilise au plus O (log (n)) opérations.
Stephen Montgomery-Smith

Je l'ai. J'ai augmenté votre réponse.
microbian

@ StephenMontgomery-Smith - comment sont ces O(log(n))opérations. Regardez à by1, votre boucle `` j '' est s_arr / g ou N - ce sont des opérations O (N)
Glenn Teitelbaum

3

C

Je ne sais pas quels sont les critères, mais comme je me suis amusé avec l'algorithme, voici mon entrée:

void rotate(int* b, int size, int shift)
{
    int *done;
    int *p;
    int i;
    int saved;
    int c;

    p = b;
    done = p;
    saved = *p;
    for (i = 0; i < size; ++i) {
        c = saved;
        p += shift;
        if (p >= b+size) p -= size;
        saved = *p;
        *p = c;
        if (p == done) {
            p += 1;
            done = p;
            saved = *p;
        }
    }
}

Je vais aussi jouer au golf pour une bonne mesure; 126 octets, peuvent être raccourcis:

void r(int*b,int s,int n){int*d,*p,i,t,c;d=p=b;t=*p;for(i=0;i<s;++i){c=t;p+=n;if(p>=b+s)p-=s;t=*p;*p=c;if(p==d){d=++p;t=*p;}}}

3

Je ne vois pas beaucoup de solutions C ++ ici, alors j'ai pensé que j'essaierais celle-ci car elle ne compte pas les caractères.

Ceci est une vraie rotation "sur place", utilise donc 0 espace supplémentaire (sauf techniquement swap et 3 pouces) et puisque la boucle est exactement N, remplit également la complexité O (N).

template <class T, size_t N>
void rot(std::array<T,N>& x, int shift)
{
        size_t base=0;
        size_t cur=0; 
        for (int i = 0; i < N; ++i)
        {
                cur=(cur+shift)%N; // figure out where we are going
                if (cur==base)     // exact multiple so we have to hit the mods when we wrap
                {
                        cur++;
                        base++;
                }
                std::swap(x.at(base), x.at(cur)); // use x[base] as holding area
        }
}

Remarque: je n'ai délibérément pas utilisé std::rotateparce que ce genre de but va à l'encontre du but
Glenn Teitelbaum

2

Si vous effectuez tour à tour chacun des cycles de rotation possibles par n (il y en a GCD (n, len (arr))), vous n'avez besoin que d'une seule copie temporaire d'un élément de tableau et de quelques variables d'état. Comme ça, en Python:

from fractions import gcd

def rotate(arr, n):
    total = len(arr)
    cycles = gcd(n, total)
    for start in range(0, cycles):
        cycle = [i % total for i in range(start, abs(n * total) / cycles, n)]
        stash = arr[cycle[-1]]
        for j in reversed(range(1, len(cycle))):
            arr[cycle[j]] = arr[cycle[j - 1]]
        arr[cycle[0]] = stash

1
Je pense que vous avez la bonne idée, mais votre cyclevariable est de taille non constante. Vous devrez générer ce tableau au fur et à mesure.
Keith Randall

2

C (137 caractères)

#include <stdio.h>

void rotate(int * array, int n, int k) {
    int todo = (1<<n+1)-1;
    int i = 0, j;
    int tmp = array[0];

    while (todo) {
        if (todo & 1<<i) {
            j = (i-k+n)%n;
            array[i] = todo & 1<<j ? array[j] : tmp;
            todo -= 1<<i;
            i = j;
        } else tmp = array[++i];
    }
}

int main() {
    int a[] = {1,2,3,4,5,6,7,8,9};
    rotate(a, 9, 4);
    for (int i=0; i<9;i++) printf("%d ", a[i]);
    printf("\n");
}

Fonction rotateréduite à 137 caractères:

void r(int*a,int n,int k){int m=(1<<n+1)-1,i=0,j,t=a[0];while(m)if(m&1<<i){j=(i-k+n)%n;a[i]=(m&1<<j)?a[j]:t;m-=1<<i;i=j;}else t=a[++i];}

2

Factor a un type intégré pour les tableaux rotatifs <circular>, il s'agit donc en fait d'une opération O (1):

: rotate ( circ n -- )
    neg swap change-circular-start ;

IN: 1 9 [a,b] <circular> dup 6 rotate >array .
{ 4 5 6 7 8 9 1 2 3 }
IN: 1 9 [a,b] <circular> dup 3 rotate >array .
{ 7 8 9 1 2 3 4 5 6 }

Un équivalent Facteur moins tricheur de l'impressionnante solution C de Ben Voigt:

: rotate ( n s -- ) 
    reverse! swap cut-slice [ reverse! ] bi@ 2drop ;

IN: 7 V{ 0 1 2 3 4 5 6 7 8 9 } [ rotate ] keep .
V{ 3 4 5 6 7 8 9 0 1 2 }

2

JavaScript 45

Je suis allé au golf de toute façon parce que j'aime le golf. Il est au maximum O (N) tant que t<= taille du tableau.

function r(o,t){for(;t--;)o.unshift(o.pop())}

Pour gérer tn'importe quelle proportion en O (N), les éléments suivants peuvent être utilisés (pesant 58 caractères):

function r(o,t){for(i=t%o.length;i--;)o.unshift(o.pop())}

Ne revient pas, modifie le tableau en place.


1
+1 pourr(o,t) => rot
Conor O'Brien

1

REBEL - 22

/_(( \d+)+)( \d+)/$3$1

Entrée: k exprimé sous la forme d'un entier unaire utilisant _comme chiffre, suivi d'un espace, puis d'un tableau d'entiers délimité par des espaces.

Sortie: un espace, puis le tableau pivote.

Exemple:

___ 1 2 3 4 5/_(( \d+)+)( \d+)/$3$1

État final:

 3 4 5 1 2

Explication:

À chaque itération, il remplace un _et un tableau [array] + tailpar tail + [array].

Exemple:

___ 1 2 3 4 5
__ 5 1 2 3 4
_ 4 5 1 2 3
 3 4 5 1 2

Je ne pense pas que ce soit O (n). La copie d'un tableau est O(n), et vous faites cela plusieurs nfois.
Ben Voigt

1

Java

public static void rotate(int[] arr, int by) {
    int n = arr.length;
    int i = 0;
    int j = 0;
    while (i < n) {
        int k = j;
        int value = arr[k];
        do {
            k = (k + by) % n;
            int tmp = arr[k];
            arr[k] = value;
            value = tmp;
            i++;
        } while (k != j);
        j++;
    }
}

Démo ici .

Javascript minimisé , 114 :

function rotate(e,r){n=e.length;i=0;j=0;while(i<n){k=j;v=e[k];do{k=(k+r)%n;t=e[k];e[k]=v;v=t;i++}while(k!=j);j++}}

1

Haskell

Il s'agit en fait de θ (n), car la division est θ (k) et la jointure est θ (nk). Pas sûr de la mémoire cependant.

rotate 0 xs = xs
rotate n xs | n >= length xs = rotate (n`mod`(length xs)) xs
            | otherwise = rotate' n xs

rotate' n xs = let (xh,xt) = splitAt n xs in xt++xh

1

Python 3

from fractions import gcd
def rotatelist(arr, m):
    n = len(arr)
    m = (-m) % n # Delete this line to change rotation direction
    for i0 in range(gcd(m, n)):
        temp = arr[i0]
        i, j = i0, (i0 + m) % n
        while j != i0:
            arr[i] = arr[j]
            i, j = j, (j + m) % n
        arr[i] = temp


Complexité temporelle O (n) de la mémoire constante



0

python

   import copy
    def rotate(a, r):
        c=copy.copy(a);b=[]
        for i in range(len(a)-r):   b.append(a[r+i]);c.pop();return b+c

La copie du tableau n'est pas un espace constant. La réponse de @ MadisonMay fait essentiellement la même chose que ce code avec beaucoup moins de caractères.
Blckknght

0

vb.net O (n) (pas de mémoire constante)

Function Rotate(Of T)(a() As T, r As Integer ) As T()     
  Dim p = a.Length-r
  Return a.Skip(p).Concat(a.Take(p)).ToArray
End Function

0

Rubis

def rotate(arr, n)
  arr.tap{ (n % arr.size).times { arr.unshift(arr.pop) } }  
end

0

C (118)

Était probablement un peu trop indulgent avec certaines des spécifications. Utilise la mémoire proportionnelle à shift % length. Peut également tourner dans le sens opposé si une valeur de décalage négative est transmise.

r(int *a,int l,int s){s=s%l<0?s%l+l:s%l;int *t=malloc(4*s);memcpy(t,a+l-s,4*s);memcpy(a+s,a,4*(l-s));memcpy(a,t,4*s);}

0

Python 2, 57

def rotate(l,n):
 return l[len(l)-n:len(l)]+l[0:len(l)-n]

Si seulement l[-n:len(l)-n]ça fonctionnait comme je m'y attendais. Il revient juste []pour une raison quelconque.


0
def r(a,n): return a[n:]+a[:n]

Quelqu'un pourrait-il s'il vous plaît vérifier si cela répond réellement aux exigences? Je pense que oui, mais je n'ai pas encore étudié le CS.


0

C ++, 136

template<int N>void rotate(int(&a)[N],int k){auto r=[](int*b,int*e){for(int t;--e>b;t=*b,*b++=*e,*e=t);};r(a,a+k);r(a+k,a+N);r(a,a+N);}

0

Java

Échangez les k derniers éléments avec les premiers k éléments, puis faites pivoter les éléments restants de k. Lorsqu'il vous reste moins de k éléments à la fin, faites-les pivoter de k% du nombre d'éléments restants. Je ne pense pas que quiconque ci-dessus ait adopté cette approche. Effectue exactement une opération de swap pour chaque élément, fait tout en place.

public void rotate(int[] nums, int k) {
    k = k % nums.length; // If k > n, reformulate
    rotate(nums, 0, k);
}

private void rotate(int[] nums, int start, int k) {
    if (k > 0) {
        if (nums.length - start > k) { 
            for (int i = 0; i < k; i++) {
                int end = nums.length - k + i;
                int temp = nums[start + i];
                nums[start + i] = nums[end];
                nums[end] = temp;
            }
            rotate(nums, start + k, k); 
        } else {
            rotate(nums, start, k % (nums.length - start)); 
        }
    }
}

0

Perl 5 , 42 octets

sub r{$a=pop;map{unshift@$a,pop@$a}1..pop}

Essayez-le en ligne!

Le sous-programme prend la distance de rotation comme premier paramètre et une référence au tableau comme deuxième. Le temps d'exécution est constant en fonction de la distance de rotation. La taille du tableau n'affecte pas le temps d'exécution. Le tableau est modifié en place en supprimant un élément de droite et en le plaçant à gauche.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.