Ruota un array intero con un algoritmo O (n) [chiuso]


10

Scrivi una funzione che ruota un array intero di un dato numero k. k elementi dalla fine dovrebbero spostarsi all'inizio dell'array e tutti gli altri elementi dovrebbero spostarsi a destra per creare lo spazio.

La rotazione dovrebbe essere eseguita sul posto.

L'algoritmo non deve essere eseguito in più di O (n), dove n è la dimensione dell'array.

Anche una memoria costante deve essere utilizzata per eseguire l'operazione.

Per esempio,

se la matrice è inizializzata con elementi arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

ruotare (arr, 3) risulterà negli elementi {7, 8, 9, 1, 2, 3, 4, 5, 6}

rotate (arr, 6) risulterà {4, 5, 6, 7, 8, 9, 1, 2, 3}


1
Cosa si intende per memoria costante qui? Sicuramente richiede almeno la memoria O (n) almeno solo per memorizzare l'array in elaborazione rendendo impossibile l'utilizzo della memoria O (1) .
Ad Hoc Garf Hunter,

2
Sto votando per chiudere questa domanda come fuori tema perché le domande senza un criterio vincente primario oggettivo sono fuori tema, in quanto rendono impossibile decidere indiscutibilmente quale voce dovrebbe vincere. Non c'è assolutamente alcun motivo per cui questo dovrebbe essere un concorso di popolarità.
James,

Votato per chiudere. Dal wiki del concorso di popolarità ( qui ), "Dà la libertà ai partecipanti di decidere cosa fare in parti cruciali e li incentiva a usare questa libertà". Non penso che lasciare aperta la sfida a nessun algoritmo sia una creatività incoraggiante per una sfida così semplice, almeno non nella misura in cui funziona come un popcon. Questo sarebbe più adatto come una sfida di code-golf .
mbomb007,

Risposte:


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

minified:

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
Avresti dovuto scrivere la condizione del ciclo while comea <-- b
solo il

Ci fu un tempo in cui i programmi C vinsero concorsi di popolarità ...
Anubian Noob,

Sei il migliore! Quanto elegante e ottimizzato .. Potresti farlo con un array di bit?

9

APL (4)

¯A⌽B
  • A è il numero di posizioni da ruotare
  • B è il nome dell'array da ruotare

Non sono sicuro che APL lo abbia effettivamente richiesto, ma nell'implementazione che ho visto (gli interni di) questo richiederebbe tempo proporzionale Ae memoria costante.


+1 se questo fosse golf :)
Glenn Teitelbaum,

Non lo fa sul posto però.
Marin

@marinus: Certamente lo fa nelle implementazioni che ho visto.
Jerry Coffin,

Come è una funzione? Potrebbe essere {⍵⌽⍨-⍺}o {⌽⍺⌽⌽⍵}. In NARS2000 può essere elegantemente scritto come ⌽⍢⌽.
Adám,

5

Ecco una versione a C tortuosa dell'idea di 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);
  }
}

Non sembra una soluzione di memoria costante, vero?
microbian

Sì, è una soluzione di memoria costante. La roba "malloced" è una copia temporanea dell'array in modo che io possa copiare i dati originali in esso ancora e ancora, in modo da poter testare diversi importi di rotazione.
Stephen Montgomery-Smith,

Ciò che ruota l'attuale è la funzione "ruota". Usa 5 numeri interi e due doppi. Chiama anche una funzione "gcd" che utilizza un numero intero e utilizza al massimo le operazioni O (log (n)).
Stephen Montgomery-Smith,

Fatto. Ho aumentato la tua risposta.
microbian

@ StephenMontgomery-Smith - come vanno queste O(log(n))operazioni. Guarda byessere 1, il tuo ciclo `j 'è s_arr / go N - questo è O (N) operazioni
Glenn Teitelbaum,

3

C

Non sono sicuro di quali siano i criteri, ma da quando mi sono divertito con l'algoritmo, ecco la mia voce:

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

Lo giocherò anche per una buona misura; 126 byte, può essere ridotto:

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

Non vedo molte soluzioni C ++ qui, quindi ho pensato di provare questo poiché non conta i caratteri.

Questa è una vera rotazione "sul posto", quindi usa 0 spazio extra (tranne tecnicamente swap e 3 ints) e poiché il loop è esattamente N, soddisfa anche la complessità 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
        }
}

Nota: non l'ho fatto apposta std::rotateperché questo tipo di sconfitte ha lo scopo
Glenn Teitelbaum,

2

Se si esegue ciascuno dei possibili cicli di rotazione di n a turno (esistono GCD (n, len (arr)) di questi), è necessaria solo una singola copia temporanea di un elemento array e alcune variabili di stato. In questo modo, in 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
Penso che tu abbia la giusta idea, ma la tua cyclevariabile ha dimensioni non costanti. Dovrai generare questo array mentre procedi.
Keith Randall,

2

C (137 caratteri)

#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");
}

Funzione rotateridotta a 137 caratteri:

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 ha un tipo incorporato per array ruotabili <circular>, quindi in realtà si tratta di un'operazione 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 equivalente fattore meno imbroglione dell'impressionante soluzione C di 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

Sono andato a golf comunque perché mi piace il golf. È al massimo O (N) purché tsia <= dimensione dell'array.

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

Per gestire tqualsiasi proporzione in O (N) è possibile utilizzare quanto segue (con un peso di 58 caratteri):

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

Non ritorna, modifica l'array in posizione.


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

1

RIBELLE - 22

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

Input: k espresso come intero unario usando _come cifra, seguito da uno spazio, quindi da una matrice di numeri interi delimitata da spazi.

Output: uno spazio, quindi l'array ruotato.

Esempio:

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

Stato finale:

 3 4 5 1 2

Spiegazione:

Ad ogni iterazione, sostituisce uno _e un array [array] + tailcon tail + [array].

Esempio:

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

Non penso che questo sia O (n). Copia di un array è O(n), e lo fai nvolte.
Ben Voigt,

1

Giava

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

Demo qui .

Javascript minimizzato , 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

Questo è in realtà θ (n), poiché la divisione è θ (k) e l'unione è θ (nk). Non sono sicuro della memoria però.

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

Memoria costante
O (n) complessità temporale



0

pitone

   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 copia dell'array non è spazio costante. La risposta di @ MadisonMay fa essenzialmente la stessa cosa di questo codice con molti meno caratteri.
Blckknght,

0

vb.net O (n) (non memoria costante)

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

Rubino

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

0

C (118)

Probabilmente era un po 'troppo indulgente con alcune delle specifiche. Usa memoria proporzionale a shift % length. Anche in grado di ruotare nella direzione opposta se viene passato un valore di spostamento negativo.

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]

Se solo l[-n:len(l)-n]funzionasse come mi aspetterei. Ritorna solo []per qualche motivo.


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

Qualcuno potrebbe verificare se questo soddisfa effettivamente i requisiti? Penso di si, ma non ho ancora studiato 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

Giava

Scambia gli ultimi k elementi con i primi k elementi, quindi ruota gli elementi rimanenti di k. Quando alla fine sono rimasti meno di k elementi, ruotali di k% numero di elementi rimanenti. Non credo che nessuno sopra abbia adottato questo approccio. Esegue esattamente un'operazione di scambio per ogni elemento, fa tutto al suo posto.

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 byte

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

Provalo online!

La subroutine prende la distanza per ruotare come primo parametro e un riferimento all'array come secondo. Il tempo di esecuzione è costante in base alla distanza di rotazione. La dimensione della matrice non influisce sul tempo di esecuzione. L'array viene modificato in posizione rimuovendo un elemento da destra e posizionandolo a sinistra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.