2011-07-06 18 views
13

Sağa ve sola doğru bir yer N konumuna göre kaydırmalıyım.C# 'de hızlı dizi kaydırma uygulaması?

Kaydettiğim tarafta çıkan öğeler diğer tarafa geri dönmelidir. Sağ 13 ile

Shift: 15 tarafından bırakılan

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

Shift: Bu işlem milyonlarca kez olacak ve gerçekten hızlı olmalı

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

.

Geçerli uygulamam aşağıdaki gibidir. Yapılacak bir optimizasyon varsa lütfen bir göz atın ve önerin.

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); 
     } 
    } 
} 
o kaymıştır alacak miktarına bir ipucu olarak

(ama optimizasyon yol açabilir şüphe):

- 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. Güvenli olmayan kod çalıştırmak için güvenlik izni alınamıyor:/

PS2: Ortaya çıkan dizi, ileri işleme için farklı bir kitaplığa doğru bir dizi olarak geçirilmelidir, bu yüzden yalnızca sarmak ve yeniden dizine sığamıyorum.

PS3: Yöntem, ref yöntemini kullandığı için aynı dizide çalışmayı tercih ediyorum ve bunu yeni bir dizide yapıyor ve sonra da kopyalama işlemi zaman alacaktır (parça için 'temp' dizisini kullanıyorum) Bu kayma nedeniyle düşüyor).

+3

Diziyi değiştirmek zorunda mısın? Dizi kaydırılmış gibi davranan bir sarıcı yapamaz mısın? – svick

+1

Güvenli olmayan kodlara ihtiyacınız yok. – SLaks

+7

Eşyaları hiç hareket ettirmemek muhtemelen daha verimli olmaz mıydı - sadece bir endeksi takip edin ve öğeleri herhangi bir yere kopyalamadan dairesel olarak erişin. – aardvarkk

cevap

13

Bunun yerine Buffer.BlockCopy kullanmalısınız. Dizi indekslemesini atlar ve hızlı hafıza kopyalarını gerçekleştirir. Unutmayın, BlockCopy bayt cinsinden veriyi dizi elemanının büyüklüğüne göre değil, bunun için hesaplamak için sizeof()'u kullandığınızdan emin olun. Eğer hiç kopyasını yapmayın bir modülo

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

kullanmak aslında bu iyi görünüyor! dizi bayt [] –

4

Sadece dizinin ilk öğesinin dizinini kaydedin. Değişen dizide değer elde etmeniz gerektiğinde, iterasyon indeksinize eklemeniz yeterlidir.
Vardiya yapmanız gerektiğinde, vardiya değerini ilk öğenin dizinine ekleyin.

+0

Ortaya çıkan dizi, kontrol edemediğim kodlara iletilecektir. Bunu soruma ekleyeceğim. Bir noktada sonuçta ortaya çıkan dizi benim tarafımdan değil, bir kütüphaneden. –

+0

Marino Šimić: bu kodun bir Array veya IEnumerable'a ihtiyacı var mı? – Andrei

+0

Byte [] ' –

0

Böyle bir şey kullanabilirsiniz. Referansı diziye ve kaydırma değerine depolayan hafif nesneler oluşturun. Daha sonra değeri almak gerektiğinde bunun yerine bu nesneleri kullanın.

Aynı dizide birkaç vardiya "yığını" oluşturmanız gerekiyorsa, durumu yalnızca bir katman oluşturmama durumuna getirin (iletilen nesneden geçişi ekleyin).Eğer gerçekten yeni, kaymıştır dizi Buffer.BlockCopy() kullanmak oluşturmak varsa

+0

olduğundan tam baytlara ihtiyacım var, bu çözümümden daha fazla memcpy işlemi içerebiliyor gibi görünüyor, bu yüzden daha yavaş olacağını düşünüyorum ... Array.Copy'nin tam olarak nasıl çalıştığını bilmiyorum. Daha az hafıza işlemi daha hızlı demektir. Ek tamponlarla problemim yok, sadece hız. –

+0

Bunu kontrol etmek ... sonuncusu mesafeye bağlı bir takas içindi, b. – FlyingStreudel

+0

, sola giderken sınır hatası veriyor. aptal modulo. ve onun sabit. – FlyingStreudel

2

ile sadece dairesel endeksi cant

4

(ı örnekte bir int dizisi kabul, herhangi bir başka türü için aynı işleri):

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

İşlemleri aynı diziden ve aynı diziye yapmak için BlockCopy kullanabilir miyim? –

+0

iyi no - henüz kopyalamadığınız dizinin bir kısmını geçersiz kılacağınız için. – BrokenGlass

+0

BlockCopy() 'nin bir alternatifi 'Array.Copy()' dır, şu anda reflektörüm kullanışlı değil, o yüzden verimli olup olmadığını bilmiyorum. Eğer yeni bir dizi oluşturmamaya çalışabilirseniz ve diğer cevapların da işaret ettiği gibi kaydırma işlemini üst düzeye taşıyan ince bir tabakaya sahip olabilirsiniz. – BrokenGlass

2

Jerry Penner's answer dayanarak:

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

Dairesel sol ve sağ kaydırmayı destekler ve yalnızca verilen dizide çalışır.