You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多维数组行/列和匹配调整失效求助(附C#实现代码)

矩阵行和列和调整问题

我有三个数组:

  • 第一个是元素为杂乱正整数的多维数组(示例为5行6列);
  • 第二个是包含6个元素的数组,对应多维数组各列的目标和;
  • 第三个是包含5个元素的数组,对应多维数组各行的目标和。

需要编写一个函数,通过偏移多维数组的元素值,使其行和与列和分别匹配第三个数组和第二个数组,且尽量不使用外部库。

我尝试编写了两个分别用于调整列和行的函数,但无法正常工作,恳请协助解决问题。

以下是我编写的C#代码:

private void AdjustColumnOfSqueakyForFutureOnMatrix(decimal[,] matrixOfQtysByDPAndInstance, List<InstanceQtyHolderItem> instanceQtyHolderItems, decimal[] totalByDP, int decimals)
{
    string matrixBeforeAdjust = GetStringViewOfMatrix(matrixOfQtysByDPAndInstance);
    int mul = 1;
    for (int i = 0; i < decimals; ++i)
    {
        mul *= 10;
    }
    decimal unit = 1 / mul;

    Logger.Info("Start Adjust Column for FutureOnMatrix");
    for (int j = 0; j <= matrixOfQtysByDPAndInstance.GetUpperBound(1); j++)
    {
        Logger.Info($"Try to Fix column {j}, rapresent element of InstanceQtyHolder idInstance: {instanceQtyHolderItems[j].IdInstance}, QtyHolder {instanceQtyHolderItems[j].QtyHolder}");
        do
        {
            decimal totalByColumn = SumByColumnOnFutureOnMatrix(matrixOfQtysByDPAndInstance, j);
            Logger.Info($"Currently we have {totalByColumn} element on column {j}, expected {totalByDP[j]}");
            if (totalByColumn == totalByDP[j])
            {
                Logger.Info($"column {j}, correct");
                break;
            }
                
            bool isToAdd = totalByDP[j] - totalByColumn > 0;

            decimal maxValueOfColumn = 0;
            int rowOfMaxValueOfColumn = -1;
            for (int ii = 0; ii <= matrixOfQtysByDPAndInstance.GetUpperBound(0); ii++)
            {
                if (matrixOfQtysByDPAndInstance[ii, j] > maxValueOfColumn)
                {
                    maxValueOfColumn = matrixOfQtysByDPAndInstance[ii, j];
                    rowOfMaxValueOfColumn = ii;
                }
            }

            if (rowOfMaxValueOfColumn == -1)
            {
                if (isToAdd)
                    rowOfMaxValueOfColumn = 0;
                else
                {
                    Logger.Warn($"In column {j}, there isn't a value that i can swap");
                    Logger.Warn($"Matrix, at the begin of the function {matrixBeforeAdjust}Matrix, at this moment {GetStringViewOfMatrix(matrixOfQtysByDPAndInstance)}");
                    break;
                }
            }   

            decimal maxValueOfRow = 0;
            int colOfMaxValueOfRow = -1;

            for (int jj = j + 1; jj <= matrixOfQtysByDPAndInstance.GetUpperBound(1); jj++)
            {
                if(SumByColumnOnFutureOnMatrix(matrixOfQtysByDPAndInstance, jj) != totalByDP[jj])
                {
                    if (matrixOfQtysByDPAndInstance[rowOfMaxValueOfColumn, jj] > maxValueOfRow)
                    {
                        maxValueOfRow = matrixOfQtysByDPAndInstance[rowOfMaxValueOfColumn, jj];
                        colOfMaxValueOfRow = jj;
                    }
                }
            }

            if (colOfMaxValueOfRow == -1)
            {
                if (!isToAdd && j < matrixOfQtysByDPAndInstance.GetUpperBound(1))
                    colOfMaxValueOfRow = j + 1;
                else
                {
                    Logger.Warn($"In column {j} there is value on row ({rowOfMaxValueOfColumn}) that i need to swap, but there isn't on another column with same row a value aviable to swap");
                    Logger.Warn($"Matrix, at the begin of the function {matrixBeforeAdjust}Matrix, at this moment {GetStringViewOfMatrix(matrixOfQtysByDPAndInstance)}");
                    break;
                }
            }

            matrixOfQtysByDPAndInstance[rowOfMaxValueOfColumn, j] += isToAdd ? unit : -unit;
            matrixOfQtysByDPAndInstance[rowOfMaxValueOfColumn, colOfMaxValueOfRow] += isToAdd ? -unit : unit;

        } while(true);
    }
}

private void AdjustRowOfSqueakyForFutureOnMatrix(decimal[,] matrixOfQtysByDPAndInstance, List<DeliveryPlanSaveEntity> sortedFutureDeliveryPlans, decimal[] totalByInstance, int decimals)
{
    string matrixBeforeAdjust = GetStringViewOfMatrix(matrixOfQtysByDPAndInstance);
    int mul = 1;
    for (int i = 0; i < decimals; ++i)
    {
        mul *= 10;
    }
    decimal unit = 1 / mul;

    Logger.Info("Start Adjust Row for FutureOnMatrix");
    for (int i = 0; i <= matrixOfQtysByDPAndInstance.GetUpperBound(0); i++)
    {
        Logger.Info($"Try to Fix row {i}, rapresent element of DeliveryPlans id: {sortedFutureDeliveryPlans[i].IdDeliveryPlan}, idArticle {sortedFutureDeliveryPlans[i].IdArticle}, week: {sortedFutureDeliveryPlans[i].Week}");
        do
        {
            decimal totalByRow = SumByRowOnFutureOnMatrix(matrixOfQtysByDPAndInstance, i);
            Logger.Info($"Currently we have {totalByRow} element on row {i}, expected {totalByInstance[i]}");
            if (totalByRow == totalByInstance[i])
            {
                Logger.Info($"row {i}, correct");
                break;
            }


            bool isToAdd = totalByInstance[i] - totalByRow > 0;

            decimal maxValueOfRow = 0;
            int columnOfMaxRowOfColumn = -1;
            for (int j = 0; j <= matrixOfQtysByDPAndInstance.GetUpperBound(1); j++)
            {
                if (matrixOfQtysByDPAndInstance[i, j] > maxValueOfRow)
                {
                    maxValueOfRow = matrixOfQtysByDPAndInstance[i, j];
                    columnOfMaxRowOfColumn = j;
                }
            }

            if (columnOfMaxRowOfColumn == -1)
            {
                if (isToAdd)
                    columnOfMaxRowOfColumn = 0;
                else
                {
                    Logger.Warn($"In row {i}, there isn't a value that i can swap");
                    Logger.Warn($"Matrix, at the begin of the function {matrixBeforeAdjust}Matrix, at this moment {GetStringViewOfMatrix(matrixOfQtysByDPAndInstance)}");
                    break;
                }
            }

            decimal maxValueOfColumn = 0;
            int rowOfMaxValueOfColumn = -1;

            for (int ii = i + 1; ii <= matrixOfQtysByDPAndInstance.GetUpperBound(0); ii++)
            {
                if (SumByRowOnFutureOnMatrix(matrixOfQtysByDPAndInstance, ii) != totalByInstance[ii])
                {
                    if (matrixOfQtysByDPAndInstance[ii, columnOfMaxRowOfColumn] > maxValueOfColumn)
                    {
                        maxValueOfColumn = matrixOfQtysByDPAndInstance[ii, columnOfMaxRowOfColumn];
                        rowOfMaxValueOfColumn = ii;
                    }
                }
            }

            if (rowOfMaxValueOfColumn == -1)
            {
                if (!isToAdd && i < matrixOfQtysByDPAndInstance.GetUpperBound(0))
                    rowOfMaxValueOfColumn = i + 1;
                else
                {
                    Logger.Warn($"In row {i} there is value on column ({columnOfMaxRowOfColumn}) that i need to swap, but there isn't on another row with same column a value aviable to swap");
                    Logger.Warn($"Matrix, at the begin of the function {matrixBeforeAdjust}Matrix, at this moment {GetStringViewOfMatrix(matrixOfQtysByDPAndInstance)}");
                    break;
                }
            }

            matrixOfQtysByDPAndInstance[i, columnOfMaxRowOfColumn] += isToAdd ? unit : -unit;
            matrixOfQtysByDPAndInstance[rowOfMaxValueOfColumn, columnOfMaxRowOfColumn] += isToAdd ? -unit : unit;

        } while (true);
    }
}

当前代码的问题

  1. 调整顺序冲突:单独调整列或行会破坏之前已调好的行和/列和。例如先调列到目标和,再调行时会改变列和,导致前序工作失效。
  2. 交换逻辑缺陷:调整列时仅修改同一行的两个元素,会改变该行总和,破坏行和正确性;同理调整行时会破坏列和。
  3. 无限循环风险:do-while(true)循环仅在列/行和完全匹配时退出,若逻辑无法收敛(如找不到合适交换项)会陷入死循环。
  4. 无意义的最大值依赖:选择最大值位置调整没有逻辑依据,反而限制调整灵活性,甚至导致无法找到可调整元素。

解决方案思路

正确做法是先调整所有行到目标行和,再在不改变行和的前提下调整列到目标列和(反之亦然)。调整列时,通过修改同一列的两个不同行元素,保持每行总和不变:

  • 给列j加单位:行i的列j元素加1单位,行k的列j元素减1单位,行i和行k总和不变,仅列j总和变化。
  • 减列j总和则反向操作。

前提条件:所有行目标和的总和必须等于所有列目标和的总和,否则问题无解。

修正后的代码

// 辅助函数:计算指定行的和
private decimal SumRow(decimal[,] matrix, int row)
{
    decimal sum = 0;
    for (int col = 0; col <= matrix.GetUpperBound(1); col++)
    {
        sum += matrix[row, col];
    }
    return sum;
}

// 辅助函数:计算指定列的和
private decimal SumColumn(decimal[,] matrix, int col)
{
    decimal sum = 0;
    for (int row = 0; row <= matrix.GetUpperBound(0); row++)
    {
        sum += matrix[row, col];
    }
    return sum;
}

// 第一步:调整所有行到目标行和
private void AdjustRowsToTarget(decimal[,] matrix, decimal[] targetRowSums, int decimals)
{
    decimal unit = 1 / (decimal)Math.Pow(10, decimals);
    int rowCount = matrix.GetUpperBound(0) + 1;
    int colCount = matrix.GetUpperBound(1) + 1;

    for (int row = 0; row < rowCount; row++)
    {
        decimal currentSum = SumRow(matrix, row);
        decimal difference = targetRowSums[row] - currentSum;

        // 处理精度误差,跳过已符合要求的行
        if (Math.Abs(difference) < unit / 2) continue;

        // 计算需要调整的单位数
        int steps = (int)Math.Round(difference / unit);

        // 遍历列调整元素,优先选择非零元素避免负数
        int colIndex = 0;
        while (steps != 0 && colIndex < colCount)
        {
            // 减少时确保元素不会变负
            if (steps < 0 && matrix[row, colIndex] + steps * unit < 0)
            {
                colIndex++;
                continue;
            }

            int adjustAmount = Math.Min(Math.Abs(steps), (int)(matrix[row, colIndex] / unit));
            if (adjustAmount == 0)
            {
                colIndex++;
                continue;
            }

            matrix[row, colIndex] += steps > 0 ? adjustAmount * unit : -adjustAmount * unit;
            steps -= steps > 0 ? adjustAmount : -adjustAmount;
        }
    }
}

// 第二步:保持行和不变,调整列到目标列和
private void AdjustColumnsToTargetPreserveRows(decimal[,] matrix, decimal[] targetColSums, int decimals)
{
    decimal unit = 1 / (decimal)Math.Pow(10, decimals);
    int rowCount = matrix.GetUpperBound(0) + 1;
    int colCount = matrix.GetUpperBound(1) + 1;

    for (int col = 0; col < colCount; col++)
    {
        decimal currentSum = SumColumn(matrix, col);
        decimal difference = targetColSums[col] - currentSum;

        // 处理精度误差,跳过已符合要求的列
        if (Math.Abs(difference) < unit / 2) continue;

        int steps = (int)Math.Round(difference / unit);

        // 寻找可调整的行对:一个可加,一个可减
        int row1 = 0;
        int row2 = 1;
        while (steps != 0 && row1 < rowCount && row2 < rowCount)
        {
            bool isAdding = steps > 0;

            // 找到可以执行加减操作的行
            while (row1 < rowCount && (isAdding && matrix[row1, col] + unit < 0 || !isAdding && matrix[row1, col] - unit < 0))
            {
                row1++;
            }
            while (row2 < rowCount && (row2 == row1 || (!isAdding && matrix[row2, col] + unit < 0 || isAdding && matrix[row2, col] - unit < 0)))
            {
                row2++;
            }

            if (row1 >= rowCount || row2 >= rowCount) break;

            // 每次调整1个单位,确保稳定性
            int adjustAmount = 1;
            if (isAdding)
            {
                matrix[row1, col] += adjustAmount * unit;
                matrix[row2, col] -= adjustAmount * unit;
            }
            else
            {
                matrix[row1, col] -= adjustAmount * unit;
                matrix[row2, col] += adjustAmount * unit;
            }
            steps -= isAdding ? adjustAmount : -adjustAmount;
        }
    }
}

// 主函数:按顺序调用调整方法
public void AdjustMatrixToTargetSums(decimal[,] matrix, decimal[] targetRowSums, decimal[] targetColSums, int decimals)
{
    // 验证总和是否相等,否则无解
    decimal totalRowSum = targetRowSums.Sum();
    decimal totalColSum = targetColSums.Sum();
    if (Math.Abs(totalRowSum - totalColSum) > (1 / (decimal)Math.Pow(10, decimals)) / 2)
    {
        Logger.Warn("行目标总和与列目标总和不相等,无法调整");
        return;
    }

    AdjustRowsToTarget(matrix, targetRowSums, decimals);
    AdjustColumnsToTargetPreserveRows(matrix, targetColSums, decimals);
}

代码说明

  1. 总和验证:先检查行目标和总和是否等于列目标和总和,这是问题有解的必要条件。
  2. 行调整:遍历每一行,调整该行内元素值到目标行和,优先选择非零元素避免出现负数。
  3. 列调整:保持行和不变,通过修改同一列的两个不同行元素,将列和调整到目标值,每次调整1个单位确保稳定性。
  4. 精度处理:使用Math.Abs(difference) < unit / 2处理浮点数精度误差,避免微小误差导致的无限循环。

内容的提问来源于stack exchange,提问作者Alberto Fagni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 23:27:35