Girar uma matriz inteira com um algoritmo O (n) [fechado]

10

Escreva uma função que gire uma matriz inteira por um determinado número k. Os elementos k do final devem se mover para o início da matriz e todos os outros elementos devem se mover para a direita para criar espaço.

A rotação deve ser feita no local.

O algoritmo não deve ser executado em mais de O (n), onde n é o tamanho da matriz.

Também é necessário usar uma memória constante para executar a operação.

Por exemplo,

se a matriz for inicializada com elementos arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}

rotate (arr, 3) fará com que os elementos sejam {7, 8, 9, 1, 2, 3, 4, 5, 6}

rotate (arr, 6) resultará em {4, 5, 6, 7, 8, 9, 1, 2, 3}

microbiano
fonte
11
O que se entende por memória constante aqui? Certamente, isso requer pelo menos O (n) memória no mínimo apenas para armazenar a matriz que está sendo processada, tornando impossível o uso da memória O (1) .
Ad Hoc Garf Hunter
2
Estou votando para encerrar esta questão como fora de tópico, porque perguntas sem um critério de objetivo primário objetivo são fora de tópico, pois tornam impossível decidir indiscutivelmente qual a entrada que deve ganhar. Não há absolutamente nenhuma razão para que este seja um concurso de popularidade.
James
Votou para fechar. No wiki do concurso de popularidade ( aqui ), "Dá liberdade aos participantes para decidir o que fazer em partes cruciais e os incentiva a usar essa liberdade". Eu não acho que deixar o desafio aberto a qualquer algoritmo conta como encorajador da criatividade para um desafio tão simples, pelo menos não na medida em que funcione como um popcon. Isso seria mais adequado como um desafio de código-golfe .
mbomb007

Respostas:

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

Minificado:

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
fonte
4
Você deveria ter escrito a condição do loop while comoa <-- b
justhalf
Costumava haver um momento em que os programas C ganhou concursos de popularidade ...
Anubian Noob
Você é o melhor! Quão elegante e otimizado .. Você poderia fazer isso com matriz de bits?
9

APL (4)

¯A⌽B
  • A é o número de lugares para girar
  • B é o nome da matriz a ser rotacionada

Não tenho certeza se o APL realmente o exigia, mas na implementação que eu vi (o interior de) isso levaria um tempo proporcional à Amemória constante.

Jerry Coffin
fonte
+1 se isso fosse golfe :)
Glenn Teitelbaum
Mas não faz isso no lugar.
marinus
@ Marinus: Certamente nas implementações que eu já vi.
perfil completo de Jerry Coffin
Como isso é uma função? Pode ser {⍵⌽⍨-⍺}ou {⌽⍺⌽⌽⍵}. No NARS2000, pode ser elegantemente escrito como ⌽⍢⌽.
Adám
5

Aqui está uma versão em C da idéia 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
fonte
Não parece uma solução de memória constante, é?
microbian
Sim, é uma solução de memória constante. O material "malloced" é uma cópia temporária da matriz, para que eu possa copiar os dados originais repetidamente, para que eu possa testar diferentes quantidades de rotação.
Stephen Montgomery-Smith
O que a rotação real é a função "girar". Ele usa 5 números inteiros e dois duplos. Ele também chama uma função "gcd" que usa um número inteiro e usa no máximo operações O (log (n)).
Stephen Montgomery-Smith
Entendi. Eu levantei sua resposta.
microbian
@ StephenMontgomery-Smith - como são essas O(log(n))operações. Olhe para byser 1, seu loop `j 'é s_arr / g ou N - essas são operações O (N)
Glenn Teitelbaum
3

C

Não tenho certeza qual é o critério, mas desde que me diverti com o algoritmo, aqui está minha entrada:

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

Também vou jogar golfe por uma boa medida; 126 bytes, podem ser reduzidos:

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
fonte
3

Não vejo muitas soluções C ++ aqui, então achei que tentaria essa, uma vez que não conta caracteres.

Esta é a rotação "in-loco" verdadeira, portanto, usa 0 espaço extra (exceto troca tecnicamente e 3 ints) e, como o loop é exatamente N, também atende à complexidade 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
fonte
Nota: Eu propositadamente não usar std::rotateporque esse tipo de derrota a finalidade
Glenn Teitelbaum
2

Se você executar cada um dos possíveis ciclos de rotações n por vez (existem GCD (n, len (arr))), será necessário apenas uma cópia temporária de um elemento da matriz e algumas variáveis ​​de estado. Assim, em 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
fonte
11
Acho que você tem a ideia certa, mas sua cyclevariável é de tamanho não constante. Você precisará gerar essa matriz à medida que avança.
perfil completo de Keith Randall
2

C (137 caracteres)

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

Função rotatereduzida para 137 caracteres:

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
fonte
2

O fator possui um tipo interno para matrizes rotativas <circular>; portanto, essa é realmente uma operação 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 }

Um fator de fator menos enganador da impressionante solução 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
fonte
2

JavaScript 45

Fui para o golfe de qualquer maneira, porque eu gosto de golfe. É no máximo O (N), contanto que tseja <= tamanho da matriz.

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

Para lidar tcom qualquer proporção em O (N), o seguinte pode ser usado (pesando 58 caracteres):

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

Não retorna, edita a matriz no lugar.

George Reith
fonte
11
+1 parar(o,t) => rot
Conor O'Brien
1

REBEL - 22

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

Entrada: k expressa como um número inteiro unário usando _como dígito, seguido por um espaço e, em seguida, uma matriz de números delimitados por espaço.

Saída: um espaço e, em seguida, a matriz girada.

Exemplo:

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

Estado final:

 3 4 5 1 2

Explicação:

Em cada iteração, ele substitui um _e uma matriz [array] + tailpor tail + [array].

Exemplo:

___ 1 2 3 4 5
__ 5 1 2 3 4
_ 4 5 1 2 3
 3 4 5 1 2
Kendall Frey
fonte
Eu não acho que isso é O (n). Copiar uma matriz é O(n), e você faz isso nalgumas vezes.
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++;
    }
}

Demonstração aqui .

Javascript reduzido , 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++}}
ValarDohaeris
fonte
1

Haskell

Na verdade, é θ (n), pois a divisão é θ (k) e a junção é θ (nk). Não tenho certeza sobre a memória embora.

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
fonte
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

Memória constante
O (n) complexidade de tempo

AMK
fonte
0

Pitão

def rotate(a, n): a[:n], a[n:] = a[-n:], a[:-n] 
Madison May
fonte
Isso usará memória constante?
precisa saber é o seguinte
Hmm ... não tenho certeza.
Madison May
Não é uma operação de memória constante.
Microbian
Shucks. Boa chamada ...
Madison May
0

Pitão

   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
fonte
Copiar a matriz não é espaço constante. A resposta do @ MadisonMay faz essencialmente a mesma coisa que esse código com muito menos caracteres.
precisa saber é o seguinte
0

vb.net O (n) (não memória 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
fonte
0

Rubi

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

C (118)

Provavelmente foi um pouco branda com algumas das especificações. Usa memória proporcional a shift % length. Também é capaz de girar na direção oposta se um valor de deslocamento negativo for passado.

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
fonte
0

Python 2, 57

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

Se ao menos l[-n:len(l)-n]funcionasse como eu esperava. Apenas retorna []por algum motivo.

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

Alguém poderia verificar se isso realmente atende aos requisitos? Eu acho que sim, mas ainda não estudei CS.

ɐɔıʇǝɥʇuʎs
fonte
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
fonte
0

Java

Troque os últimos k elementos pelos primeiros k elementos e gire os elementos restantes por k. Quando você tiver menos de k elementos no final, gire-os pelo número de k% de elementos restantes. Acho que ninguém acima adotou essa abordagem. Executa exatamente uma operação de troca para cada elemento, faz tudo no lugar.

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
fonte
0

Perl 5 , 42 bytes

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

Experimente online!

A sub-rotina leva a distância para girar como o primeiro parâmetro e uma referência à matriz como o segundo. O tempo de execução é constante com base na distância da rotação. O tamanho da matriz não afeta o tempo de execução. A matriz é modificada no local removendo um elemento da direita e colocando-o à esquerda.

Xcali
fonte