2011-07-06 11 views
13

Necesito desplazar hacia la derecha y hacia la izquierda una matriz en N lugares.¿Implementación de cambio de matriz rápida en C#?

Los elementos que salen en el lado donde cambio deben volver al otro lado.

Shift derecho 13:

[0,1,2,3,4,5,6,7,8,9] -> [7,8,9,0,1,2,3,4,5,6] 

Shift dejado por 15:

[0,1,2,3,4,5,6,7,8,9] -> [5,6,7,8,9,0,1,2,3,4] 

Esta operación sucede a millones de veces y debe ser muy rápido.

Mi implementación actual es la siguiente. Eche un vistazo y sugiera si hay alguna optimización que hacer.

if (shift > 0) 
{ 
    int offset = array.Length % shift; 
    if (offset > 0) 
    { 
     byte[] temp = new byte[offset]; 
     if (!right) 
     { 
      Array.Copy(array, temp, offset); 
      Array.Copy(array, offset, array, 0, array.Length - offset); 
      Array.Copy(temp, 0, array, array.Length - offset, temp.Length); 
     } 
     else 
     { 
      Array.Copy(array, array.Length - offset, temp, 0, offset); 
      Array.Copy(array, 0, array, offset, array.Length - offset); 
      Array.Copy(temp, 0, array, 0, temp.Length); 
     } 
    } 
} 

Como un consejo sobre cuánto va a obtener cambiado (pero dudo que puede conducir a la optimización):

- depends on the entropy of the array itself 
- for aray that are full of same values it will get shifted roughtly 0 
- more entropy means higher shift value 
- direction of shift will be used generally more to the left 

PS. No se puede obtener el permiso de seguridad para ejecutar código inseguro:/

PS2: la matriz resultante debe pasarse como una matriz hacia adelante a una biblioteca diferente para su posterior procesamiento, por lo que no puedo simplemente envolver y reindexar.

PS3: Prefiero trabajar en la misma matriz ya que el método usa ref, y hacer eso en una nueva matriz y luego copiar de nuevo tomaría mucho tiempo (estoy usando la matriz 'temp' para la parte que se cae debido al cambio).

+3

¿De verdad necesita cambiar la matriz? ¿No puedes hacer una envoltura que actúe como si la matriz se hubiera cambiado? – svick

+1

No necesita un código inseguro. – SLaks

+7

¿No sería posiblemente más eficiente no mover los elementos en absoluto, simplemente hacer un seguimiento de un índice y acceder de forma circular a los elementos sin copiarlos en ningún lugar? – aardvarkk

Respuesta

13

En su lugar, debe usar Buffer.BlockCopy. Pasa por alto la indexación de la matriz y realiza copias de memoria rápidas. Tenga en cuenta que BlockCopy copia los datos en bytes, no en términos del tamaño del elemento de la matriz, así que asegúrese de usar sizeof() para dar cuenta de eso.

+0

¡esto realmente parece bueno! Necesito exactamente bytes ya que la matriz es byte [] –

4

Simplemente guarde el índice del primer elemento de la matriz. Cuando necesite obtener valor en una matriz desplazada, simplemente agréguela a su índice de iteración.
Cuando necesite hacer un cambio, agregue el valor de desplazamiento al índice del primer elemento.

+0

La matriz resultante se transferirá al código que no puedo controlar. Añadiré esto a mi pregunta. En algún momento, la matriz resultante no será procesada por mí sino por una biblioteca. –

+0

Marino Šimić: ¿ese código necesita una Matriz o un Enumerable? – Andrei

+0

Necesita 'byte []' –

0

usted podría utilizar algo como esto si usted no puede simplemente índice de circular con un módulo

public byte[] ShiftBytes(byte[] arr, int shift, bool isRight) 
{ 
    byte[] newBytes = new byte[arr.Length]; 
    shift = (isRight) ? shift : -1 * shift; 
    for (int i = 0; i < arr.Length; i++) 
     newBytes[i] = arr[(((i + shift) % arr.Length) 
      + arr.Length) % arr.Length]; 
    return newBytes; 
} 
+0

Esto parece contener más operaciones memcpy que mi solución, así que realmente creo que sería más lento ... No conozco cómo funciona Array.Copy exactamente, pero en general menos operaciones de memoria significa más velocidad. No tengo ningún problema con los búferes adicionales, solo con la velocidad. –

+0

Al revisar este ... el último fue para un intercambio basado en la distancia, mi b. – FlyingStreudel

+0

límites de error cuando va a la izquierda. estúpido modulo. y es fijo – FlyingStreudel

2

No hacer la copia en absoluto. Cree objetos livianos que almacenen la referencia a la matriz y el valor de desplazamiento. Luego use esos objetos cuando necesite recuperar el valor.

Si necesita "apilar" un par de turnos en la misma matriz, optimice la situación creando solo una capa (agregue el desplazamiento del objeto pasado).

4

Si realmente tiene que crear una nueva, desplazado variedad utilizar Buffer.BlockCopy() (I asumido una matriz int en el ejemplo, mismas obras de cualquier otro tipo):

int [] sourceArray = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
int [] targetArray = new int[sourceArray.Length]; 

int shift = 13; 
int offset = shift % sourceArray.Length; 

Buffer.BlockCopy(sourceArray, offset*sizeof(int), targetArray, 0 , (sourceArray.Length - offset)*sizeof(int)); 
Buffer.BlockCopy(sourceArray, 0, targetArray, (sourceArray.Length - offset) * sizeof(int), offset * sizeof(int)); 
+0

¿Puedo usar BlockCopy para realizar las operaciones desde y hacia la misma matriz? –

+0

, bueno, no, ya que anularía parte de la matriz que aún no ha copiado. – BrokenGlass

+0

Una alternativa a 'BlockCopy()' es 'Array.Copy()', pero no tengo el reflector a mano ahora, así que no sé si es eficiente. Si puede, evitaría crear una nueva matriz y solo tendría una capa delgada en la parte superior que maneja el cambio, como apuntan algunas de las otras respuestas. – BrokenGlass

2

Basado en Jerry Penner's answer:

internal static void ShiftCircular(int offset, object[] array) 
{ 
    if (offset == 0 || array.Length <= 1) 
     return; 

    offset = offset % array.Length; 

    if (offset == 0) 
     return; 

    if (offset < 0) 
     offset = array.Length + offset; 

    Array.Reverse(array, 0, array.Length); 
    Array.Reverse(array, 0, offset); 
    Array.Reverse(array, offset, array.Length - offset); 
} 

Es compatible con el desplazamiento circular hacia la izquierda y hacia la derecha y funciona solo en la matriz dada.

Cuestiones relacionadas