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}

microbien
la source
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);}
Ben Voigt
la source
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.

Jerry Coffin
la source
+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);
  }
}
Stephen Montgomery-Smith
la source
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;}}}
treamur
la source
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
        }
}
Glenn Teitelbaum
la source
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
Colin Watson
la source
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];}
craesh
la source
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 }
Björn Lindqvist
la source
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.

George Reith
la source
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
Kendall Frey
la source
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++}}
Valar Dohaeris
la source
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
archaephyrryx
la source
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

AMK
la source
0

Python

def rotate(a, n): a[:n], a[n:] = a[-n:], a[:-n] 
Madison May
la source
Cela utilisera-t-il une mémoire constante?
SztupY
Hmm ... pas sûr.
Madison May
Ce n'est pas une opération de mémoire constante.
microbian
Shucks. Bon appel ...
Madison May
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
saykou
la source
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
Adam Speight
la source
0

Rubis

def rotate(arr, n)
  arr.tap{ (n % arr.size).times { arr.unshift(arr.pop) } }  
end
OI
la source
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);}
cardinaliti
la source
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.

cjfaure
la source
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.

ɐɔıʇǝɥʇuʎs
la source
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);}
mattnewport
la source
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)); 
        }
    }
}
Matthew Saltz
la source
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.

Xcali
la source