在C#中查找行元素之间的差异

3

我是一名新手,对该页面和编程都很陌生。
让我提出我的问题。 我有一个数组,比如说

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),最后选择平均值最小的那一行。

有没有更有效的方法呢?(我在考虑使用 LINQ,但不知道如何正确使用它)。如果有任何帮助,将不胜感激。提前谢谢!!!

编辑: 感谢大家的答案……它们非常有用,并帮助我理解了如何处理我的问题。我看到你们大多数人建议使用列表(List)。这就为我带来了另一个问题(我只有一点编程知识):如何将 int[,] 数组转换为 List?这是可能的吗?应该先将 int[,] 转换为 int[][],然后再转换为 List 吗? 再次感谢您的答案和时间。

我创建了以下函数来将 2d 数组转换为 List…但似乎它不能正常工作。如果能得到任何帮助,将不胜感激。

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

        int[,] res = result;

        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行的差异,因为与平均值相比,差异会更耗费时间(或者至少取决于一行中有多少项)。 - AndrewC
我可能没有表达清楚我的问题。我的意思是要计算每行差异的平均值。 - tasos
@tasos 好的,我理解你的意思是你想遍历每一行,计算差值,然后找到每一行的平均差值? - Purplegoldfish
一行中是否总是有3个元素? - AndrewC
不,那只是一个例子,我们事先不知道数组的长度。 - tasos
显示剩余4条评论
8个回答

2
希望您能从中受益,虽然这种方法非常简陋。基本上,它将您的锯齿数组转换为数组列表,然后在单独的方法中计算平均值,并按平均值排序并取一个结果。
值得注意的是,我的解决方案始终尝试存储差异的正值(因为这似乎是您正在做的),即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();
        }

我正在尝试解决这里提供的所有解决方案,由于我是新手,所以有问题。如果我理解您的解决方案正确,您建议将值传递到交错数组中,然后将其转换为列表并进行处理。我的问题是数组是另一个函数的输出,我不知道数组的大小。我已经尝试在网络上查找如何将int[ , ]数组转换为List<int[]>,但我没有找到有效的方法...您有什么建议吗? - tasos

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()扩展方法即可。


1

试试这个

    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。


1

基本上这就是你要找的东西。我没有测试代码以确保它是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];

非常感谢您的回答。但是我该如何将int[,]数组转换为List<int[]>?因为.ToList方法或Cast.ToList都不起作用... - tasos
现在它使用了一个二维数组。 - A.R.

1
如果性能真的很重要,考虑使用平坦的 int 数组来存储行(您不需要 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);

请注意,上面的代码已经针对性能进行了优化。但是这也显然地影响了可读性
希望这可以帮到你。

1

这里有一些代码,其中有一些注释,我在里面做了一些奇怪的事情。我认为你已经有很多有趣的答案了,但我没有看到任何简单明了的东西,所以这是我的尝试。

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

1
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.

非常感谢您的解决方案。但是我发布的数组只是一个示例,不幸的是,我事先不知道数组A [i,j]的列数或行数。因此,我正在寻找适用于不同大小数组的解决方案。再次感谢。 - tasos
使用N = A.GetLength(0)来查找矩阵的行数。 - John Alexiou

1
如果我要解决这个问题,我会改变数据存储的方式。
首先,我会创建一个名为myNumbers(或其他名称)的新类,该类包含一个整数数组和一个名为average的属性,只有get方法(您不希望手动更改此值)。您还可以创建一个构造函数,该函数接受一个整数数组,用于在创建类的新实例时使用。(这个新类代表您当前拥有的数组中的1行)
您的名为average的属性将是一个int,它将循环遍历数组并计算平均值,然后返回它。 http://msdn.microsoft.com/en-us/library/aa288470(v=vs.71).aspx C#属性教程。
现在,在您的主代码中,您想摆脱数组,改为使用List,并使用myNumbers类的新实例填充它。
现在,您所要做的就是创建一个for each循环并遍历列表,您可以检查列表中每个项目的average属性以获取平均值,或者您可以完全忽略循环并使用LINQ选择最高值。

http://msdn.microsoft.com/en-us/library/bb383799(v=vs.90).aspx Linq教程(网络上还有很多)

我认为上述方法最适合您的需求,因为您需要存储平均值等信息。它还可以让您练习类/属性/循环/列表等,因为您是编程新手。

如果您尝试了一些代码仍然感到困难,请在此更新您的帖子,我们将尽力帮助您。我想先不用代码回答这个问题,这样您至少可以自己尝试做一下。


@tasos 祝你好运!当你完成后,请发布你的代码,我相信人们会愿意提出改进意见等:)欢迎来到SO - Purplegoldfish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接