web-dev-qa-db-fra.com

C # moyen le plus rapide de changer de tableau

Comment puis-je déplacer rapidement tous les éléments d'un tableau vers la gauche, en remplissant la fin avec null?

Par exemple, [0,1,2,3,4,5,6] deviendrait [1,2,3,4,5,6, null]

Edit: J'ai dit rapidement mais je suppose que je voulais dire efficacement. Je dois le faire sans créer une liste ou une autre structure de données. C’est quelque chose que je dois faire plusieurs centaines de milliers de fois en un minimum de temps.

53
Dested

Voici mon harnais de test ...

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

Voici les résultats de performance pour 1 million d'itérations de chaque solution (Intel Core 8 Xeon E5450 à 3,00 GHz)

                       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

Faites le choix pour vous-même :)

108
Jason Punyon

Le moyen le plus rapide consiste à utiliser Array.Copy, qui, dans la dernière implémentation, utilise une opération de transfert de mémoire en bloc (similaire à 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 }

Edité: selon la documentation:

Si sourceArray et destinationArray se chevauchent, cette méthode se comporte comme si les valeurs d'origine de sourceArray étaient conservées dans un emplacement temporaire avant que destinationArray ne soit écrasé.

Donc, si vous ne voulez pas allouer un nouveau tableau, vous pouvez transmettre le tableau d'origine à la fois pour la source et la destination - bien que j'imagine que le compromis sera un peu plus lent, car les valeurs passent par une position temporaire.

Je suppose que, comme dans toute enquête de ce type, vous devriez procéder à une analyse comparative rapide.

57
Ben M

Voici ma solution, similaire à celle de Task en ce qu’il s’agit d’un simple wrapper Array et qu’il faut du temps O(1) pour déplacer le tableau à gauche.

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

Lancer le code de test de 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);

Prend ~ 29ms, quelle que soit la taille du tableau.

11
tdaines

Utilisez la méthode Array.Copy() comme dans

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

Le Array.Copy est probablement le moyen, Microsoft voulait nous faire copier des éléments de tableau.

7
MartinStettner

Ne pourriez-vous pas utiliser System.Collections.Generic.Queue au lieu d'un tableau?

Je pense que vous devez effectuer des actions sur votre valeur, de la supprimer, donc utiliser une file d'attente semble plus approprié:

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

Si absolument doit être dans un tableau, je recommanderais le code le plus évident possible.

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

Cela donne à l'optimiseur le plus d'opportunités pour trouver la meilleure façon, quelle que soit la plate-forme cible sur laquelle le programme est installé.

MODIFIER:

Je viens d'emprunter le code de test de Jason Punyon, et j'ai bien peur qu'il ait raison. Array.Copy gagne!

    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 prend entre 103 et 150 ms sur ma machine.

la boucle prend entre 269 et 338 ms sur ma machine.

5

Pour toute âme pour trouver ce fil et sur le point de mettre en œuvre l'une des réponses les mieux notés. Tous sont des ordures, je ne sais pas pourquoi. Peut-être que Dested a tout d’abord demandé une nouvelle implémentation de tableau ou quelque chose qui a maintenant été retiré de la question. Eh bien, si vous voulez simplement déplacer le tableau et que vous n’avez pas besoin d’un nouveau tableau, voyez une réponse comme celle de taines. Et lisez des articles tels que le tampon circulaire/tampon circulaire: http://en.wikipedia.org/wiki/Circular_buffer . Aucun déplacement des données réelles n'est nécessaire. Les performances de déplacement d'un tableau doivent not être liées à la taille du tableau.

5
user1594138

Tu ne peux pas

  • allouer le tableau avec 1000 éléments supplémentaires

  • avoir une variable entière int base = 0

  • au lieu d'accéder à a[i] accéder à a[base+i]

  • pour faire votre quart de travail, dites simplement base++

Ensuite, après l'avoir fait 1000 fois, copiez-le et recommencez.
Ainsi, vous ne faites la copie qu’une fois par tranche de 1 000 équipes.


Vieille blague:
Q: Combien d’IBM 360 faut-il pour décaler un registre d’un bit?
A: 33. 32 pour maintenir les bits en place et 1 pour déplacer le registre. (ou quelque chose comme ça ...)

4
Mike Dunlavey

Vous pouvez utiliser le même tableau comme source et destination pour une copie rapide sur place:

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

Vous pourriez le faire comme ceci:

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

Bien sûr, il serait plus efficace de supprimer complètement le tableau et d’utiliser une liste <>. Vous pouvez alors oublier la première ligne et la dernière ligne et le faire comme ceci:

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

Non testé ce code, mais il devrait décaler toutes les valeurs à droite. Notez que vous n'avez besoin que des trois dernières lignes de code pour déplacer efficacement le tableau. 

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

Si vous possédez la mémoire, vous pouvez envisager d'utiliser Code non sécurisé et de bons indicateurs à l'ancienne. 

Créez-vous un flux de mémoire et verrouillez-le ou utilisez Marshal.AllocHGlobal Construisez tous vos tableaux avec un peu de bourrage au début et à la fin. incrémente ou décrémente tous les pointeurs du tableau à la fois. Vous aurez toujours besoin de reboucler et de définir vos valeurs nulles. 

Si vous avez besoin d'incrémenter ou de décrémenter les tableaux, vous devez ajouter un remplissage entre eux.

Les tableaux sont des structures de données de bas niveau incroyablement bas. Si vous les traitez de façon bas niveau, vous pouvez en tirer des performances énormes.

Un tel circuit pourrait faire mieux que Jason avec tous ses copieurs 8 Core Intel Xeon E5450 @ 3.00GHz

1
user2163234

Je sais que c’est une vieille question, mais venant de Google, il n’y avait pas d’exemple simple. C’est donc le moyen le plus simple de réorganiser une liste, et vous n’avez pas à fournir le type, cela fonctionnera à l’exécution

   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

Essaye ça! en utilisant Linq. Pas besoin de second tableau.

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

Sortie: [0,1,2,3,4,5,6] deviendrait [1,2,3,4,5,6, null]

1
Sid

La copie de tableau est une opération O(n) et crée un nouveau tableau . Bien que la copie de tableau puisse certainement être effectuée rapidement et efficacement, le problème que vous avez déclaré peut en réalité être résolu de manière totalement différente comme vous l'avez demandé) créer un nouveau tableau/structure de données et ne créer qu'une seule petite instance d'objet d'habillage par tableau:

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

En encapsulant et contrôlant l'accès au tableau de cette manière, nous pouvons maintenant montrer comment le problème a été résolu avec un appel de méthode 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() );

Produira la sortie:

Tableau de base: [0, 1, 2, 3, 4, 5, 6]
Tableau décalé: [0, 2, 3, 4, 5, 6, null]

Je suis prêt à parier qu'il y aura une raison pour laquelle une telle solution ne fonctionnera pas pour vous, mais je pense que cela correspond aux exigences initiales énoncées. 8)

Il est souvent utile de réfléchir à toutes les sortes de solutions à un problème avant de mettre en œuvre un problème spécifique. Ce peut être la chose la plus importante que cet exemple puisse démontrer.

J'espère que cela t'aides!

0
Task

La méthode la plus efficace et la plus efficace consiste à utiliser la fonction Buffer.BlockCopy . Vous allez définir la source et la destination de votre tableau, le décalage de la source est 1. Selon votre type de tableau (je suppose que c'est int 1 int = 4 octets, vous devez donc indiquer 4 comme deuxième paramètre de cette fonction. Notez que le décalage est un décalage d'octet. 

Donc ça ressemble à ça:

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

Réponse incorrecte et légèrement amusante (merci, je serai là toute la nuit!)

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; 

Dans l’esprit d’utiliser toujours Skip (ne me demandez pas, je connais la pire utilisation de méthodes d’extension LINQ de tous les temps), la seule façon pour moi de la réécrire serait

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

Mais ce n'est AUCUN moyen plus rapide que les autres options.

0
Stan R.