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}


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

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.


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

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

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

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

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

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 }

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.


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

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

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

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



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

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

0

Rubi

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

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

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.


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.


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

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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.