2

我正在尝试使用Microsoft Solver Foundation来优化我遇到的涉及矩阵乘法的问题。我可以使用 Excel 的求解器来做到这一点,但我正在尝试将它集成到 C# 中并且遇到了麻烦。下面是一个例子的描述:

假设您有一个 (3x3) 矩阵y,定义为:

Double[][] y = 
{
    new Double[] { 5, 1, 0 },
    new Double[] { 1, 9, 1 },
    new Double[] { 0, 1, 9 },
};

我想找到 (1x3) 矩阵x使得:x * y * x'最小化。此外,x值的总和必须为 1,并且任何x值都不能小于 0。

这是我到目前为止的代码:

 SolverContext context = SolverContext.GetContext();             // Get context environment
 Model model = context.CreateModel();                            // Create a new model

 Decision d1 = new Decision(Domain.RealNonnegative, "d1");       // First item in "x" vector (must be >= 0)
 Decision d2 = new Decision(Domain.RealNonnegative, "d2");       // Second item in "x" vector (must be >= 0)
 Decision d3 = new Decision(Domain.RealNonnegative, "d3");       // Third item in "x" vector (must be >= 0)
 model.AddDecisions(d1, d2, d3);                                 // Add these to the model (this is where the outputs will be stored)

 model.AddConstraints("limits",                                  // Add constraints
     0 <= d1 <= 1,                                               // Each item must be between 0 and 1
     0 <= d2 <= 1,
     0 <= d3 <= 1,
     d1 + d2 + d3 == 1);                                         // All items must add up to 1

我坚持的部分是当你告诉它你想要最小化的内容时:

 model.AddGoal("min", GoalKind.Minimize, /* What goes here? */);

这部分通常包含一个方程(例如d1 * d2 + d3),但矩阵乘法并不是那么简单。

我可以创建一个执行乘法并返回 a 的函数double,但AddGoal()需要一个Term对象,而且我还必须对Decision对象进行算术运算。

或者,我可以将这个乘法分解为一个巨大的string表达式(我已经完成了),但如果我不必这样做,我会更喜欢。(这个字符串看起来像"d1 * 5 + d2 * 1 + d3 * 0 ...":)

有任何想法吗?谢谢。

PS:正确答案(根据Excel)是:

d1 = 0.503497
d2 = 0.216783
d3 = 0.27972

注意:解决方案必须具有可扩展性以具有n“决策”数量

4

1 回答 1

1

以下产生预期的解决方案:

using System;
using Microsoft.SolverFoundation.Services;

namespace akMSFStackOverflow
{
    class Program
    {
        static void Main(string[] args)
        {
            Double[,] y = 
            {
                { 5, 1, 0 },
                { 1, 9, 1 },
                { 0, 1, 9 },
            };

            Term goal;
            Term[,] tx;
            Term[,] ty;

            SolverContext context = SolverContext.GetContext();             // Get context environment
            Model model = context.CreateModel();                            // Create a new model

            Decision d1 = new Decision(Domain.RealNonnegative, "d1");       // First item in "x" vector (must be >= 0)
            Decision d2 = new Decision(Domain.RealNonnegative, "d2");       // Second item in "x" vector (must be >= 0)
            Decision d3 = new Decision(Domain.RealNonnegative, "d3");       // Third item in "x" vector (must be >= 0)
            model.AddDecisions(d1, d2, d3);                                 // Add these to the model (this is where the outputs will be stored)

            model.AddConstraints("limits",                                  // Add constraints
                0 <= d1 <= 1,                                               // Each item must be between 0 and 1
                0 <= d2 <= 1,
                0 <= d3 <= 1,
                d1 + d2 + d3 == 1);                                         // All items must add up to 1

            ty = matrix(y);
            tx = new Term[,] { { d1, d2, d3 } };

            goal = matMult(matMult(tx, ty), transpose(tx))[0, 0];

            model.AddGoal("goal", GoalKind.Minimize, goal);

            // Specifying the IPM solver, as we have a quadratic goal 
            Solution solution = context.Solve(new InteriorPointMethodDirective());


            Report report = solution.GetReport();
            Console.WriteLine("x {{{0}, {1}, {2}}}", d1, d2, d3);
            Console.Write("{0}", report); 

        }


        static Term[,] matrix(Double[,] m)
        {
            int rows = m.GetLength(0);
            int cols = m.GetLength(1);
            Term[,] r = new Term[rows, cols];

            for (int row = 0; row < rows; row++)
                for (int col = 0; col < cols; col++)
                    r[row, col] = m[row, col];

            return r;
        }

        static Term[,] matMult(Term[,] a, Term[,] b)
        {
            int rows = a.GetLength(0);
            int cols = b.GetLength(1);
            Term[,] r = new Term[rows, cols];

            for (int row = 0; row < rows; row++)
                for (int col = 0; col < cols; col++)
                {
                    r[row,col] = 0;
                    for (int k = 0; k < a.GetLength(1); k++)
                    {
                        r[row, col] += a[row, k] * b[k, col];
                    }
                }

            return r;
        }

        static Term[,] transpose(Term[,] m)
        {
            int rows = m.GetLength(0);
            int cols = m.GetLength(1);
            Term[,] r = new Term[cols, rows];

            for (int row = 0; row < rows; row++)
                for (int col = 0; col < cols; col++)
                {
                    r[col, row] = m[row, col];
                }

            return r;
        }
    }
}
于 2012-12-21T22:33:12.730 回答