最小化列表上的特殊功能的总和

时间:2009-01-06 15:19:51

标签: algorithm language-agnostic optimization

假设我有一个列表,我希望它被排列,以便在其连续元素上运行的某个函数的总和最小。

例如,考虑整个列表中连续对{ 1, 2, 3, 4 }的列表a^b和总和(a,b)。即。 1^2 + 2^3 + 3^4 = 90。通过检查,当列表排列为{ 2, 3, 1, 4 } => (2^3 + 3^1 + 1^4 = 12)时,实现最小总和。

请注意,总和不是循环的(即我不考虑last^first)并且顺序很重要(2^3 != 3^2)并且a^b可以是在任意数量的连续元素上运行的任何函数

这样的算法是否有名称,是否有确定的实现方法?

编辑:我重写了这个问题,因为我错误地将此标记为排序问题。正如所指出的,这更像是一个优化问题。

10 个答案:

答案 0 :(得分:5)

“排序”通常使用二进制比较运算符(“小于或等于”)来定义。您正在寻找的是列表的“最佳”排列,其中“最佳”被定义为在整个列表上定义的标准(而“特定函数”在邻居元素上定义,整个列表上的总和使它成为一个全球财产。)

如果我理解正确,“旅行推销员”就是您问题的一个实例,所以无论如何您的问题都是NP完全的; - )

答案 1 :(得分:3)

由于所使用的功能没有重复

  

a ^ b也可以是在任意数量的连续元素上运行的任何函数。

如果使用常数函数(比如alwasys返回1),所有订单的总和将相同,但在查看所有订单之前,您不一定知道。

所以我看不到比评估所有排列的函数和总和更快的东西。

(您可以记住每个元组的结果以加快评估速度,但我认为您仍然需要全部查看它们)

编辑:此外,因为它可能是一个作用于所有元素的函数,所以你可以有一个函数为除了一个之外的所有排列返回0,并为它返回1。

因此,对于一般情况,您肯定需要在所有排列上评估函数。

答案 2 :(得分:2)

这似乎是Optimization Probelm,而不是排序问题。

我打赌一点点(或许很多)工作,有人可以证明这在功能上等同于着名的NP完整问题之一。但是,对于某些特定功能(例如示例中的^ b),问题可能会更容易。

答案 3 :(得分:2)

这是家庭作业吗?

如果没有,则是动态编程问题。要查看它,您应该使用您的示例作为基础将您的问题转换为以下问题。你是在开始。您可以选择{1,2,3,4}中的任何一个。从那里你可以选择去{1,2,3,4}。这样做4次,你就可以完成{1,2,3,4}列表长度4的所有安排。

现在您需要一个成本函数,定义为:

f(prev, next) = prev ^ next
              = 0 if the solution is not valid for your original problem 
              = 0 if prev is the start

总费用表示为

cost(i|a|X) = min(i in {1,2,3,4}, f(i, a) + cost(X))

请注意i|a|X表示以元素a开头的列表,然后是i,列表的其余部分是X.

查看cost函数,您应该认识到动态编程。

从那里你可以得到一个算法。查看维基百科的introduction to dynamic programming

我可以使用PLT Scheme测试的Scheme实现是:

(define (cost lst f)
  (if (null? lst)
      0
      (let ((h (car lst))
            (t (cdr lst)))
        (if (null? t)
            0
            (+ (f h (car t))
               (cost t f))))))

(define (solve lst f)
  (let loop ((s '()))
    (if (= (length s) (length lst))
        s
        (loop
         (let choose ((candidate lst)
                      (optimal #f)
                      (optimal-cost #f))
           (if (null? candidate)
               optimal
               (let ((c (car candidate)))
                 (if (memq c s)
                     (choose (cdr candidate) optimal optimal-cost)
                     (if (not optimal) 
                         (choose (cdr candidate) (cons c s) (cost (cons c s) f))
                         (if (<= (cost (cons c s) f)
                                 (cost optimal f))
                             (choose (cdr candidate) (cons c s) (cost (cons c s) f))
                             (choose (cdr candidate) optimal optimal-cost)))))))))))

然后调用(solve '(1 2 3 4) expt)产生另一个最小解决方案'(3 2 1 4)。

答案 4 :(得分:2)

我只看到一个解决方案:

蛮力

    public static int Calculate(Func<int, int, int> f, IList<int> l)
    {
        int sum = 0;
        for (int i = 0; i < l.Count-1; i++)
        {
            sum += f(l[i], l[i + 1]);
        }
        return sum;
    }

    public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> list, int count)
    {
        if (count == 0)
        {
            yield return new T[0];
        }
        else
        {
            int startingElementIndex = 0;
            foreach (T startingElement in list)
            {
                IEnumerable<T> remainingItems = AllExcept(list, startingElementIndex);

                foreach (IEnumerable<T> permutationOfRemainder in Permute(remainingItems, count - 1))
                {
                    yield return Concat<T>(
                        new T[] { startingElement },
                        permutationOfRemainder);
                }
                startingElementIndex += 1;
            }
        }
    }

    // Enumerates over contents of both lists.
    public static IEnumerable<T> Concat<T>(IEnumerable<T> a, IEnumerable<T> b)
    {
        foreach (T item in a) { yield return item; }
        foreach (T item in b) { yield return item; }
    }

    // Enumerates over all items in the input, skipping over the item
    // with the specified offset.
    public static IEnumerable<T> AllExcept<T>(IEnumerable<T> input, int indexToSkip)
    {
        int index = 0;
        foreach (T item in input)
        {
            if (index != indexToSkip) yield return item;
            index += 1;
        }
    }

    public static void Main(string[] args)
    {
        List<int> result = null;
        int min = Int32.MaxValue;
        foreach (var p in Permute<int>(new List<int>() { 1, 2, 3, 4 }, 4))
        {
            int sum = Calculate((a, b) => (int)Math.Pow(a, b), new List<int>(p));
            if (sum < min)
            {
                min = sum;
                result = new List<int>(p);
            }
        }
        // print list
        foreach (var item in result)
        {
            Console.Write(item);
        }
    }

我从Ian Griffiths blog窃取了排列代码。

答案 5 :(得分:1)

这是我到目前为止所拥有的。我创建了一个Calc类,我可以将每个组合传递给它,然后计算总数并使用ToString()方法,因此您不必担心迭代输出和字符串和值。您可以获取构造函数中传入的total和list。然后,您可以将每个组合集添加到一个列表中,您可以按 inst 中的LINQ排序.Total ...正如我所演示的那样。仍在研究生成每种组合的方法......

class Calc
{
    private int[] items;
    private double total;
    public double Total 
    { 
        get
        { 
            return total; 
        } 
    }
    public int[] Items
    {
        get { return items;  }
        set { total = Calculate(value); }
    }
    public static double Calculate(int[] n)
    {
        double t = 0;
        for (int i = 0; i < n.Length - 1; i++)
        {
            int a = n[i]; int b = n[i + 1];
            t += a^b;
        }
        return t;
    }
    public Calc(int[] n)
    {
        this.items = n;
        this.total = Calculate(n);
    }
    public override string ToString()
    {
        var s = String.Empty;
        for (int i = 0; i < items.Length - 1; i++)
        {
            int a = items[i]; int b = items[i + 1];
            s += String.Format("{0}^{1}", a, b);
            s += i < items.Length - 2 ? "+" : "=";
        }
        s += total;
        return s;
    }
}

然后我们在计算中使用该类,并快速按每个排列的总数排序:

class Program
{
    static void Main(string[] args)
    {
        var Calculations = new List<Calc>();

        ////Add a new item to totals for every combination of...working on this
        Calculations.Add(new Calc(new int[] { 1, 2, 3, 4 }));
        //...

        //Grab the item with the lowest total... if we wanted the highest, we'd
        //just change .First() to .Last()
        var item = Calculations.OrderBy(i=>i.Total).First();
        Console.WriteLine(item);
        //Or if we wanted all of them:
        //Calculations.OrderBy(i=>i.Total).ForEach(Console.WriteLine);
    }
}

答案 6 :(得分:1)

这绝对不是排序列表。如果你有一个排序列表[x~0~ ... x~n~],列表 根据定义,[x~0~..x~i-1~,x~i + 1~..x~n~](即x~i~去除)也将被分类。在您的示例中,从子序列100,0,100中删除0很可能会使列表无效。

答案 7 :(得分:1)

这是一个NP完全问题,因为算法是未知的(NB对于给定的函数a ^ b它不是NP完全,它可以在排序后通过一次完成,参见下面的示例解决方案)< / p>

如果没有为列表的所有可能排列计算给定函数的结果,就可以预先编写“排序算法”。

然而,一旦给出了一个函数,你可以(可能)设计一个排序方法,例如对于上面的a ^ b,如此排序列表(不将函数应用于任何项目)“Max,min,next max,next min。 。 “并扭转这种秩序。

根据给定函数的复杂性,提供优化的排序例程将越来越困难。

谢谢,

答案 8 :(得分:0)

嗯,这是一个有趣的问题。使用现有的排序结构(IComparable)将无法正常工作,因为您需要的信息比compareTo方法可用的信息更多。

对此的解决方案将在很大程度上取决于您要用于排序的方法。但是,乍一看,您似乎可能需要迭代所有可能的订单才能找到最小订单。如果当前总和大于先前的总和,则可能使其短路。

我必须先试一试,看看我能想出什么。

答案 9 :(得分:0)

未经测试且未经证实:

  1. 对列表进行排序
  2. 从排序列表中创建对,抓取第一个数字和最后一个,然后是第二个和第二个到最后一个,等等(即1,2,3,4变为1,4和2,3)
  3. 对于每对,保存它是一个^ b值。对此列表进行反向排序
  4. 将新列表中的a ^ b值替换为a&amp; b值
  5. 我猜它不会那么简单......但它适用于你的例子,并不是真正重要的事情吗?