Найти разницу между элементами строк в C#

#c# #arrays #list

#c# #массивы #Список

Вопрос:

Я новичок на этой странице, а также новичок в программировании.
Позвольте мне изложить мою проблему. У меня есть массив, скажем

 1 2 3

4 5 6

7 8 9 

3 2 1
 

то, что я хочу сделать, это вычислить для каждой строки разницу между всеми элементами.

 {Math.Abs(1-2)=1

Math.Abs (2-3)=1

Math.Abs (1-3)=2}
 

для первой строки. Затем я хочу найти среднее значение для каждой строки (в первой (1 1 2)/3) и, наконец, сохраните строку с наименьшим средним значением.

Каков наиболее эффективный способ сделать это??(Я думал, что perphaps LINQ может работать, но я не знаю, как правильно использовать LINQ). Любая помощь будет оценена. Заранее спасибо всем!!!!

РЕДАКТИРОВАТЬ: Спасибо всем за ответы … они действительно полезны и помогли мне понять, как я должен обрабатывать свою проблему. Я вижу, что большинство из вас рекомендовали использовать List . Итак, это поднимает еще одну проблему для меня (помните, мало знаний в программировании) как я могу преобразовать массив int[,] в список, возможно ли это?? Должен ли я сначала преобразовать int[,] в int[][], а затем в List ? Еще раз спасибо за ваши ответы и ваше время.

Я создал следующую функцию для преобразования 2d-массива в список .. но, похоже, она работает неправильно..Любая помощь будет по-настоящему оценена..

  public static List<int[]> GetMyNumbers(int[,] result)
    {

        int[,] res = resu<

        int length = res.GetUpperBound(0)   1;
        int width = res.GetUpperBound(1)   1;
        int[] rows = new int[width];
        List<int[]> numberArrays = new List<int[]>();

        for (int i = 0; i < result.Length / width; i  )
        {
            for (int k = 0; k < width; k  )
            {
                rows[k] = res[i, k];
                Console.Write(rows[k]);
            }
            Console.WriteLine();
            numberArrays.Add(rows);//this doesn't fill the list properly..It adds to all list items the last row of the 2d array

        }

        return numberArrays;


    }
 

Комментарии:

1. Если вы отбрасываете все остальные строки, кроме строки с наименьшим средним значением, нужно ли вам вычислять различия для них? Если нет, то эффективный способ — сначала отбросить строки, а затем вычислить различия для 1 оставшейся строки, поскольку различия будут занимать больше времени, чем средние значения (или, по крайней мере, в зависимости от того, сколько элементов находится в строке).

2. возможно, я недостаточно четко сформулировал свой вопрос. что я хочу сделать, это вычислить среднее значение различий для каждой строки..

3. @tasos хорошо, я правильно вас понимаю, что вы просто хотите просмотреть каждую строку, вычислить разницу, а затем найти среднюю разницу в каждой строке?

4. @Purplegoldfish да, это именно то, что я хочу сделать .. вычислите разницу между всеми элементами строки, а затем найдите среднюю разницу..

5. всегда ли в строке 3 элемента?

Ответ №1:

Это очень грубо, но, надеюсь, вы сможете извлечь из этого пользу. По сути, он преобразует ваш неровный массив в список массивов, а затем вычисляет среднее значение отдельным методом, упорядочивает результаты по среднему значению и принимает единицу.

Стоит отметить, что мое решение всегда пытается сохранить положительное значение разницы (поскольку, похоже, это то, что вы делали), т. Е. Вместо 1 и 3, дающих -2, оно дает 2.

Я убежден, что есть более чистый способ создать это решение, но это лучшее, что я мог придумать на данный момент.

 static void Main(string[] args)
        {
            int[][] multi = new int[3][];
            multi[0] = new int[3] { 1, 2, 3 };
            multi[1] = new int[3] { 5, 10, 20 };
            multi[2] = new int[3] { 3, 4, 8 };
            List<int[]> t = multi.ToList();
            List<int[]> avg = t.OrderBy(x => GetAvgDifference(x)).Take(1).ToList();
        }

        public static double GetAvgDifference(int[] arr)
        {
            List<int> differences = new List<int>();
            for (int i = 0; i < arr.Length; i  )
            {
                for (int j = i; j < arr.Length; j  )
                {
                    int difference = arr[i] - arr[j];

                    if (difference < 1)
                    {
                        differences.Add(difference * -1);
                    }
                    else
                    {
                        differences.Add(difference);
                    }
                }
            }

            return differences.Average();
        }
 

Комментарии:

1. Я пытаюсь проработать все решения, приведенные здесь, и, поскольку я новичок, у меня есть вопросы. Если я хорошо понял ваше решение, вы предлагаете передать значения в неровный массив, а затем преобразовать его в список и вместо этого обработать список. Моя проблема в том, что массив является результатом другой функции, и я заранее не знаю размер массива. Я пытался найти через сеть, что я могу сделать, чтобы преобразовать int[ ,] array в List<int[]> но я не нашел ничего, что сработало .. у вас есть какие-либо предложения??

Ответ №2:

Вы можете использовать LINQ подобным образом для массива только с одной строкой :

 int[] tab = { 1, 2, 3 };
var tuples = tab.Select((number, index) => new Tuple<int, int>(index, number));
var average = tuples.SelectMany(t => tuples.Where(current => current.Item1 > t.Item1), (t1, t2) => Math.Abs(t1.Item2 - t2.Item2)).Average();
 

Вам нужно просто применить то же самое ко всем строкам вашего массива, а затем использовать метод расширения Min().

Ответ №3:

Вот то, что вы ищете в основном. Нет, я не тестировал код, чтобы убедиться, что он на 100% правильный, но, по крайней мере, должен заставить вас двигаться в правильном направлении.

   // Look, a two dimensional version.
  int[][] numberArray = GetMyNumbers();

  // Placeholder for the lowest averge / corresponding index.
  double lowestAvg = double.MaxValue;
  int lowestIndex = -1;

  for (int rowIndex = 0; rowIndex < numberArray.Length; rowIndex  )
  {

    {
      int[] row = numberArray[rowIndex];
      int n = row.Length;
      int[] diffs = new int[(n * n) - n];

      // Get all of the differences.
      int count = 0;
      for (int i = 0; i < n; i  )
      {
        for (int j = i   1; j < n; j  )
        {
          diffs[count] = Math.Abs(row[i] - row[j]);
          count  ;
        }
      }

      // Average them..
      double sum = 0;
      for (int i = 0; i < diffs.Length; i  )
      {
        sum  = diffs[i];
      }
      double avg = sum / diffs.Length;

      // Compare to the lowest value, making note of a new low.
      if (avg < lowestAvg)
      {
        lowestAvg = avg;
        lowestIndex = rowIndex;
      }
    }

  }

  // Now that we are here, we know which index has the lowest average of differences.
  // Do whatever you want with it.
  int[] TheAnswer = numberArray[lowestIndex];
 

Комментарии:

1. большое вам спасибо за ваш ответ.. но как я могу преобразовать int[,] array в List<int []> ?? потому что . Метод ToList не работает или не используется. ToList…

2. Вот, теперь он использует 2D-массив.

Ответ №4:

Если бы я решил это, я бы изменил способ хранения ваших данных.

Сначала я бы создал новый класс с именем myNumbers (или что-то еще), этот класс должен содержать массив целых чисел и свойство с именем average только с помощью метода get (вы не хотите иметь возможность вручную изменять это значение. Вы также можете создать конструктор, который принимает массив целых чисел, который будет использоваться при создании новых экземпляров класса. (этот новый класс представляет 1 строку в имеющемся у вас массиве)

Ваше свойство с именем average будет int , оно будет перебирать массив и вычислять среднее значение, а затем возвращать его.

http://msdn.microsoft.com/en-us/library/aa288470 (v = против 71). учебное пособие по свойствам aspx C #.

Теперь в вашем основном коде вы хотите избавиться от массива, вместо этого создайте список и заполните его новыми экземплярами вашего класса myNumbers.

Теперь все, что вам нужно сделать, это создать для каждого цикла и просмотреть список, вы можете проверить среднее свойство каждого элемента в списке, чтобы получить среднее значение, или вы можете вообще игнорировать цикл и использовать LINQ для выбора наивысшего значения

http://msdn.microsoft.com/en-us/library/bb383799 (v = против 90).учебное пособие по aspx Linq (в сети есть еще много других)

Я думаю, что приведенный выше подход лучше всего подойдет для ваших нужд, поскольку вам нужно хранить средние значения и т. Д. Это также дает вам некоторую практику с классами / свойствами / циклами / списками и т. Д., Поскольку вы новичок в программировании.

Если вы попробовали какой-то код и все еще испытываете трудности, пожалуйста, обновите свой пост здесь, и мы постараемся помочь. Я думал, что сначала отвечу на это без кода, так что, по крайней мере, вы можете попробовать сделать это самостоятельно и т.д.

Комментарии:

1. @tasos удачи! Когда вы закончите, опубликуйте свой код, и я уверен, что люди захотят предложить улучшения и т. Д. 🙂 И добро пожаловать в SO

Ответ №5:

Попробуйте это

     var rows = new List<List<int>>(){
            new List<int>(){1, 2, 3},
            new List<int>(){4, 5, 6},
            new List<int>(){7, 8, 9},
            new List<int>(){3, 2, 1}};

    var averages = new List<double>();
    foreach(var list in rows)
    {
        var diffs = new List<int>();
        for (int i = 0; i < list.Count - 1; i  )
            for (int j = i 1; j < list.Count; j  )
                diffs.Add(Math.Abs(list[i]-list[j]));
        averages.Add(diffs.Average());
    }
    averages.ForEach(i=>Console.WriteLine(i));
    Console.WriteLine("Minimum average is "   averages.Min());
 

Все ваши последовательности отличаются аналогичным образом, поэтому в этом примере все средние значения будут равны 1,3333

Ответ №6:

Если производительность действительно важна, рассмотрите возможность использования плоского массива int для хранения ваших строк (у вас нет накладных расходов на память a List<int> ). Затем выполните итерацию по плоскому массиву, увеличив ваш текущий индекс на длину строки, а затем вычислите среднее значение для текущей строки.

Вот небольшой пример:

 int rowLen = 3;
int numberOfRows = 3;
int[] rowValues = new int[rowLen * numberOfRows];

float[] avgs = new float[numberOfRows];

// First row
rowValues[0] = 1;
rowValues[1] = 2;
rowValues[2] = 3;

// Second row
rowValues[3] = 6;
rowValues[4] = 5;
rowValues[5] = 6;

// Third row
rowValues[6] = 7;
rowValues[7] = 8;
rowValues[8] = 9;

float currMinAvg = float.MaxValue;
int minIdx = -1;
int currRow = 0; 
for (int i = 0; i <= numberOfRows * rowLen - rowLen; i  = rowLen)
{    
  avgs[currRow] = 0;
  int c = 0;
  for (int k = i; k < i   rowLen-1; k  )
  {
    for (int p = k   1; p < i   rowLen; p  )
    {
      c  ;
      //Console.Out.WriteLine("calc: rowValues[{0}] - rowValues[{1}]", k, p);
      avgs[currRow]  = Math.Abs(rowValues[k] - rowValues[p]);
    }
  }

  //Console.Out.WriteLine(avgs[currRow]);
  avgs[currRow] /= c;

  if (avgs[currRow] < currMinAvg)
  {
    minIdx = i;
    currMinAvg = avgs[currRow];
  }
  currRow  ;
}

Console.Out.WriteLine("Min row indexs: {0}, min average = {1}", minIdx, currMinAvg);
 

Обратите внимание, приведенный выше код оптимизирован для повышения производительности. Это явно ухудшает читаемость.

Надеюсь, это поможет.

Ответ №7:

Вот некоторый код с несколькими комментариями, где я делаю странные вещи. Я думаю, у вас уже есть много интересных ответов, но я не видел ничего простого, так что вот моя попытка.

 static void Main(string[] args)
{
    // quickest way to initialize your input
    var input = new int[][]{
        new int[]{1, 2, 3},
        new int[]{4, 5, 6},
        new int[]{7, 8, 9},
        new int[]{3, 2, 1}
    };

    /* to get the average,
     * 1. add up all the differences
     * 2. divide by m choose 2 where m is the length of a row
     */

    // helpful factorial functoid
    Func<int, int> factorial = null;
    factorial = (n => (n > 1) ? n * factorial(n - 1) : 1);
    var mChoose2 = factorial(input[0].Length) / (2 * factorial(input[0].Length - 2));

    var getAverageOfDifferencesFunctoid = new Func<int[], double>(
        row => row.Select(
            (number1, indexInRow1) => row.Select(
                (number2, indexInRow2) => indexInRow2 > indexInRow1 ? Math.Abs(number1 - number2) : 0
                // add up all the differences for number1 with the rest of the array
            ).Sum()
        // add up all the sums of all the differences
        ).Sum()
        // divide by the number of differences
        / (double)mChoose2
    );

    // use the functoid defined above to calculate the average of differences for each row and pick the minimum
    Console.WriteLine(input.Select(row => getAverageOfDifferencesFunctoid(row)).Min());
}
 

Ответ №8:

 double[,] A = { { 1, 2, 3 }, { 4, 5, 6 } , { ... } ... };
// Initialize array of averages
double[] R = new double[N] // N is number of rows
// Calculate averages for each row
for(int i=0; i<N; i  )
{
    R[i] = (Math.Abs(A[i,0]-A[i,1]) Math.Abs(A[i,1]-A[i,2]) Math.Abs(A[i,2]-A[i,0]))/3;
}
// Find the best value
double R_min = R.Max();
// Find the index where values equals the min.
int k = R.Select((r, i) => r == R_min ? i : N).Min();
// Now A[k,*] contains the values you want to keep.
 

Комментарии:

1. большое вам спасибо за ваше решение .. но опубликованный мной массив был примером, к сожалению, я заранее не знаю количество столбцов или строк массива A [i, j] . Итак, я ищу что-то, что будет работать для разных размеров массива. но еще раз спасибо..

2. затем используйте N = A.GetLength(0) , чтобы найти количество строк.