C#中的快速数组移位实现?

时间:2011-07-06 20:30:51

标签: c# algorithm optimization

我需要在N个位置向右和向左移动一个数组。

我转移到的那一侧弹出的物品必须回到另一侧。

向右移动13:

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

左转15:

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

此操作将发生数百万次,并且必须非常快。

我目前的实施如下。请查看并建议是否需要进行一些优化。

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

作为关于它将被转移多少的提示(但我怀疑它可以导致优化):

-  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。无法获得运行不安全代码的安全权限:/

PS2:结果数组必须作为数组向前传递到不同的库以进行进一步处理,所以我不能只是换行和重新索引。

PS3:我更喜欢在同一个数组上工作,因为该方法使用ref,并且在新数组上执行此操作然后复制将非常耗时(我正在使用'temp'数组对于因转移而失败的部分。

6 个答案:

答案 0 :(得分:13)

您应该使用Buffer.BlockCopy代替。它绕过数组索引并执行快速内存复制。请注意,BlockCopy以字节为单位复制数据,而不是根据数组元素的大小进行复制,因此请务必使用sizeof()来解释数据。

答案 1 :(得分:4)

只需保存数组第一个元素的索引即可。当您需要在移位数组中获取值时,只需将其添加到迭代索引中即可 当您需要进行移位时,将移位值添加到第一个元素的索引。

答案 2 :(得分:4)

如果你真的需要创建一个新的移位数组,请使用Buffer.BlockCopy()(我假设示例中有一个int数组,同样适用于任何其他类型):

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

答案 3 :(得分:2)

根本不要复制。创建轻量级对象,用于存储对数组的引用和移位值。然后在需要检索值时使用这些对象。

如果你需要在同一个数组上“堆叠”几个班次,优化我只创建一个图层的情况(添加传入对象的移位)。

答案 4 :(得分:2)

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

它支持圆形左移和右移,仅适用于给定的数组。

答案 5 :(得分:0)

如果你不能使用模数

的圆形索引,你可以使用这样的东西
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;
}