web-dev-qa-db-ja.com

C#配列をシフトする最も簡単な方法

配列内のすべての項目を1つ左にすばやくシフトして、末尾にnullを埋め込むにはどうすればよいですか?

たとえば、[0,1,2,3,4,5,6]は[1,2,3,4,5,6、null]になります

編集:私はすぐに言ったが、私は効率的に意味したと思います。リストや他のデータ構造を作成せずにこれを行う必要があります。これは、可能な限り短い時間で数十万回行う必要があるものです。

53
Dested

これが私のテストハーネスです...

var source = Enumerable.Range(1, 100).Cast<int?>().ToArray();
var destination = new int?[source.Length];

var s = new Stopwatch();
s.Start();
for (int i = 0; i < 1000000;i++)
{
    Array.Copy(source, 1, destination, 0, source.Length - 1);
}
s.Stop();
Console.WriteLine(s.Elapsed);

各ソリューションの100万回の反復のパフォーマンス結果を以下に示します(8コアIntel Xeon E5450 @ 3.00GHz)

                       100 elements  10000 elements
For Loop                     0.390s         31.839s 
Array.Copy()                 0.177s         12.496s
Aaron 1                      3.789s         84.082s
Array.ConstrainedCopy()      0.197s         17.658s

自分で選択してください:)

110
Jason Punyon

quickestこれを行う方法は、Array.Copyを使用することです。これは、最終的な実装でバルクメモリ転送操作を使用します(memcpyと同様)。

var oldArray = new int?[] { 1, 2, 3, 4, 5, 6 };
var newArray = new int?[oldArray.Length];
Array.Copy(oldArray, 1, newArray, 0, oldArray.Length - 1);
// newArray is now { 2, 3, 4, 5, 6, null }

編集:ドキュメントによると:

SourceArrayとdestinationArrayが重複する場合、このメソッドは、destinationArrayが上書きされる前に、sourceArrayの元の値が一時的な場所に保存されているかのように動作します。

したがって、新しい配列を割り当てたくない場合は、ソースと宛先の両方に元の配列を渡すことができますが、値は一時的な保持位置を通過するため、トレードオフはパフォーマンスがやや遅くなると思います。

この種の調査のように、簡単なベンチマークを行う必要があると思います。

57
Ben M

タスクは、単純な配列ラッパーであり、配列を左にシフトするのにO(1)時間を要する)という点で、私のソリューションに似ています。

public class ShiftyArray<T>
{
    private readonly T[] array;
    private int front;

    public ShiftyArray(T[] array)
    {
        this.array = array;
        front = 0;
    }

    public void ShiftLeft()
    {
        array[front++] = default(T);
        if(front > array.Length - 1)
        {
            front = 0;
        }
    }

    public void ShiftLeft(int count)
    {
        for(int i = 0; i < count; i++)
        {
            ShiftLeft();
        }
    }

    public T this[int index]
    {
        get
        {
            if(index > array.Length - 1)
            {
                throw new IndexOutOfRangeException();
            }

            return array[(front + index) % array.Length];
        }
    }

    public int Length { get { return array.Length; } }
}

Jason Punyonのテストコードを実行する...

int?[] intData = Enumerable.Range(1, 100).Cast<int?>().ToArray();
ShiftyArray<int?> array = new ShiftyArray<int?>(intData);

Stopwatch watch = new Stopwatch();
watch.Start();

for(int i = 0; i < 1000000; i++)
{
    array.ShiftLeft();
}

watch.Stop();

Console.WriteLine(watch.ElapsedMilliseconds);

配列サイズに関係なく、〜29msかかります。

11
tdaines

Array.Copy()メソッドを次のように使用します

int?[] myArray = new int?[]{0,1,2,3,4};
Array.Copy(myArray, 1, myArray, 0, myArray.Length - 1);
myArray[myArray.Length - 1] = null

Array.Copyはおそらく、Microsoftが配列要素をコピーすることを望んでいた方法です...

7
MartinStettner

配列の代わりにSystem.Collections.Generic.Queueを使用できませんでしたか?

私はあなたがあなたの値でアクションを実行する必要があるように感じます。

// dummy initialization
        System.Collections.Generic.Queue<int> queue = new Queue<int>();
        for (int i = 0; i < 7; ++i ) { queue.Enqueue(i); }// add each element at the end of the container

        // working thread
        if (queue.Count > 0)
            doSomething(queue.Dequeue());// removes the last element of the container and calls doSomething on it
7
Seb

絶対にhasが配列にある場合、可能な限り最も明白なコードをお勧めします。

for (int index = startIndex; index + 1 < values.Length; index++)
     values[index] = values[index + 1];
values[values.Length - 1] = null;

これにより、オプティマイザーは、プログラムがインストールされているターゲットプラットフォームに関係なく、最適な方法を見つけることができます。

編集:

私はジェイソン・プニヨンのテストコードを借りましたが、彼は正しいと思います。 Array.Copyが勝ちます!

    var source = Enumerable.Range(1, 100).Cast<int?>().ToArray();
    int indexToRemove = 4;

    var s = new Stopwatch();
    s.Start();
    for (int i = 0; i < 1000000; i++)
    {
        Array.Copy(source, indexToRemove + 1, source, indexToRemove, source.Length - indexToRemove - 1);
        //for (int index = indexToRemove; index + 1 < source.Length; index++)
        //    source[index] = source[index + 1]; 
    }
    s.Stop();
    Console.WriteLine(s.Elapsed);

私のマシンでは、Array.Copyは103〜150ミリ秒かかります。

私のマシンではforループに269〜338 msかかります。

5

このスレッドを見つけて、評価の高い答えの1つを実装しようとしている魂のために。それらはすべてゴミです。それが理由はわかりません。たぶん、Destedは最初に新しい配列の実装を求めたのか、それとも質問から削除された何かを求めたのかもしれません。単純に配列をシフトするだけで、新しい配列が必要ない場合は、tdainesの答えのような答えを参照してください。そして、Circular Buffer/Ring Bufferのようなものを読んでください: http://en.wikipedia.org/wiki/Circular_buffer 。実際のデータを移動する必要はありません。配列をシフトするパフォーマンスは、not配列のサイズに関連付けられる必要があります。

5
user1594138

できません

  • 追加の1000要素を含む配列を割り当てます

  • 整数変数int base = 0を持っている

  • a[i]にアクセスする代わりにa[base+i]にアクセスします

  • シフトを行うには、base++と言うだけです

次に、これを1000回繰り返した後、コピーして最初からやり直します。
そのようにして、1000シフトにつき1回だけコピーを行います。


古いジョーク:
Q:レジスタを1ビットシフトするのにどれくらいのIBM 360が必要ですか?
A:33. 32はビットを所定の位置に保持し、1はレジスタを移動します。 (またはそのような...)

4
Mike Dunlavey

コピー元とコピー先として同じ配列を使用して、高速インプレースコピーを実行できます。

static void Main(string[] args)
        {
            int[] array = {0, 1, 2, 3, 4, 5, 6, 7};
            Array.ConstrainedCopy(array, 1, array, 0, array.Length - 1);
            array[array.Length - 1] = 0;
        }
3
user215054

次のようにすることができます。

var items = new int?[] { 0, 1, 2, 3, 4, 5, 6 };  // Your array
var itemList = new List<int?>(items);  // Put the items in a List<>
itemList.RemoveAt(1); // Remove the item at index 1
itemList.Add(null); // Add a null to the end of the list
items = itemList.ToArray(); // Turn the list back into an array

もちろん、配列を完全に取り除き、List <>を使用する方が効率的です。次に、最初の行と最後の行を忘れて、次のようにします。

var itemList = new List<int?> { 0, 1, 2, 3, 4, 5, 6 };
itemList.RemoveAt(1); // Remove the item at index 1
itemList.Add(null); // Add a null to the end of the list
2
Aaron

このコードはテストされていませんが、すべての値を1つずつ右にシフトする必要があります。コードの最後の3行が、配列を効率的にシフトするために必要なすべてであることに注意してください。

public class Shift : MonoBehaviour {
     //Initialize Array
     public int[] queue;

     void Start () {
          //Create Array Rows
          queue = new int[5];

          //Set Values to 1,2,3,4,5
          for (int i=0; i<5;i++)
          {
               queue[i] = i + 1;
          }

          //Get the integer at the first index
          int prev = queue[0];

          //Copy the array to the new array.
          System.Array.Copy(queue, 1, queue, 0, queue.Length - 1);

          //Set the last shifted value to the previously first value.
          queue[queue.Length - 1] = prev;
1
Ben

メモリを所有している場合は、 安全でないコード と古風なポインタを使用することを検討できます。

自分でメモリストリームを作成し、ロックダウンするか、Marshal.AllocHGlobalを使用します。先頭と末尾に少しパディングを入れて、すべての配列を構築します。すべての配列ポインターを一度にインクリメントまたはデクリメントします。ループバックしてnullを設定する必要があります。

配列を選択的にインクリメントまたはデクリメントする必要がある場合、それらの間にパディングを追加する必要があります。

配列は非常に低レベルのデータ構造です。低レベルの方法で配列を処理すると、配列から大きなパフォーマンスを引き出すことができます。

これを行うベイトレールは、8コアIntel Xeon E5450 @ 3.00GHzのすべてのコピーでJasonを上回る可能性があります

1
user2163234

私はこれが古い質問であることを知っていますが、Googleから来た簡単な例はありませんでしたので、これのおかげでリストを並べ替える最も簡単な方法であり、実行時にうまくいくタイプを指定する必要はありません、

   private static List<T> reorderList<T>(List<T> list){
       List<T> newList = new List<T>();

       list.ForEach(delegate(T item)
       {
           newList.Add(item);
       });

       return newList;
   }
1
Martin Barker

これを試して! Linqを使用します。 2番目の配列は必要ありません。

        var i_array = new int?[] {0, 1, 2, 3, 4, 5, 6 };

        i_array = i_array.Select((v, k) => new { v = v, k = k }).
            Where(i => i.k > 0).Select(i => i.v).ToArray();

        Array.Resize(ref i_array, i_array.Length + 1);

出力:[0,1,2,3,4,5,6]は[1,2,3,4,5,6、null]になります

1
Sid

配列のコピーはO(n)操作であり、新しい配列を作成します。配列のコピーは確かに迅速かつ効率的に実行できますが、あなたが述べた問題は実際にはまったく異なる方法で解決できます新しい配列/データ構造を作成せずに(要求したとおり)、配列ごとに1つの小さなラッピングオブジェクトインスタンスのみを作成します。

using System;
using System.Text;

public class ArrayReindexer
{
    private Array reindexed;
    private int location, offset;

    public ArrayReindexer( Array source )
    {
        reindexed = source;
    }

    public object this[int index]
    {
        get
        {
            if (offset > 0 && index >= location)
            {
                int adjustedIndex = index + offset;
                return adjustedIndex >= reindexed.Length ? "null" : reindexed.GetValue( adjustedIndex );
            }

            return reindexed.GetValue( index );
        }
    }

    public void Reindex( int position, int shiftAmount )
    {
        location = position;
        offset = shiftAmount;
    }

    public override string ToString()
    {
        StringBuilder output = new StringBuilder( "[ " );
        for (int i = 0; i < reindexed.Length; ++i)
        {
            output.Append( this[i] );
            if (i == reindexed.Length - 1)
            {
                output.Append( " ]" );
            }
            else
            {
                output.Append( ", " );
            }
        }

        return output.ToString();
    }
}

この方法で配列へのアクセスをラップして制御することにより、O(1)メソッド呼び出しで問題が解決された方法を示すことができます...

ArrayReindexer original = new ArrayReindexer( SourceArray );
Console.WriteLine( "   Base array: {0}", original.ToString() );
ArrayReindexer reindexed = new ArrayReindexer( SourceArray );
reindexed.Reindex( 1, 1 );
Console.WriteLine( "Shifted array: {0}", reindexed.ToString() );

出力を生成します:

基本配列:[0、1、2、3、4、5、6]
シフトされた配列:[0、2、3、4、5、6、null]

そのような解決策がうまくいかない理由があるのではないかと思いますが、これは最初に述べられた要件と一致すると思います。 8)

特定の問題を実装する前に、問題の解決策のすべての異なるkindsについて考えることは、しばしば役に立ちます。おそらく、この例が示すことができる最も重要なことかもしれません。

お役に立てれば!

0
Task

私が信じる最良かつ最も効率的な方法は、Buffer.BlockCopy関数を使用することです。送信元と送信先の両方を配列に設定します。送信元のオフセットは1です。配列のタイプに応じて(intと仮定)、1 int = 4バイトなので、この2番目のパラメーターとして4を渡す必要があります。関数。オフセットはバイトオフセットであることに注意してください。

したがって、次のようになります。

int bytes2copy = yourArray.length - 4;
Buffer.BlockCopy(yourArray, 4, yourArray, 0, bytes2copy);
yourArray[yourArray.length-1] = null;
0
Reza Ghochkhani
using System;
using System.Threading;

namespace ShiftMatrix
{
    class Program
    {
        static void Main(string[] args)
        {

            MatrixOperation objMatrixOperation = new MatrixOperation();

            //Create a matrix
            int[,] mat = new int[,]
        {
        {1, 2},
        {3,4 },
        {5, 6},
        {7,8},
        {8,9},
        };

            int type = 2;
            int counter = 0;
            if (type == 1)
            {
                counter = mat.GetLength(0);
            }
            else
            {
                counter = mat.GetLength(1);
            }
            while (true)
            {
                for (int i = 0; i < counter; i++)
                {
                    ShowMatrix(objMatrixOperation.ShiftMatrix(mat, i, type));
                    Thread.Sleep(TimeSpan.FromSeconds(2));
                }
            }
        }
        public static void ShowMatrix(int[,] matrix)
        {
            int rows = matrix.GetLength(0);
            int columns = matrix.GetLength(1);
            for (int k = 0; k < rows; k++)
            {
                for (int l = 0; l < columns; l++)
                {
                    Console.Write(matrix[k, l] + " ");
                }
                Console.WriteLine();
            }
        }
    }
    class MatrixOperation
    {
        public int[,] ShiftMatrix(int[,] origanalMatrix, int shift, int type)
        {
            int rows = origanalMatrix.GetLength(0);
            int cols = origanalMatrix.GetLength(1);

            int[,] _tmpMatrix = new int[rows, cols];
            if (type == 2)
            {
                for (int x1 = 0; x1 < rows; x1++)
                {
                    int y2 = 0;
                    for (int y1 = shift; y2 < cols - shift; y1++, y2++)
                    {
                        _tmpMatrix[x1, y2] = origanalMatrix[x1, y1];
                    }
                    y2--;
                    for (int y1 = 0; y1 < shift; y1++, y2++)
                    {
                        _tmpMatrix[x1, y2] = origanalMatrix[x1, y1];
                    }
                }
            }
            else
            {
                int x2 = 0;
                for (int x1 = shift; x2 < rows - shift; x1++, x2++)
                {
                    for (int y1 = 0; y1 < cols; y1++)
                    {
                        _tmpMatrix[x2, y1] = origanalMatrix[x1, y1];
                    }
                }
                x2--;
                for (int x1 = 0; x1 < shift; x1++, x2++)
                {
                    for (int y1 = 0; y1 < cols; y1++)
                    {
                        _tmpMatrix[x2, y1] = origanalMatrix[x1, y1];
                    }
                }

            }
            return _tmpMatrix;
        }
    }

}
0
Rajesh Barfa

間違った、ちょっとおかしい答え(おかげで、一晩中ここにいます!)

int?[] test = new int?[] {0,1,2,3,4,5,6 };

        int?[] t = new int?[test.Length];
        t = test.Skip(1).ToArray();
        t[t.Length - 1] = null; 

まだSkipを使用するという精神で(私に聞いてはいけない、私はLINQ拡張メソッドの最悪の使用法を知っている)、それを書き換えようと思った唯一の方法は

        int?[] test = new int?[] { 0, 1, 2, 3, 4, 5, 6 };

        int?[] t = new int?[test.Length];
        Array.Copy(test.Skip(1).ToArray(), t, t.Length - 1);

しかし、それは他のオプションよりも速い方法ではありません。

0
Stan R.