假设我有一个列表,我希望它被排列,以便在其连续元素上运行的某个函数的总和最小。
例如,考虑整个列表中连续对{ 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
可以是在任意数量的连续元素上运行的任何函数
这样的算法是否有名称,是否有确定的实现方法?
编辑:我重写了这个问题,因为我错误地将此标记为排序问题。正如所指出的,这更像是一个优化问题。
答案 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)
对此的解决方案将在很大程度上取决于您要用于排序的方法。但是,乍一看,您似乎可能需要迭代所有可能的订单才能找到最小订单。如果当前总和大于先前的总和,则可能使其短路。
我必须先试一试,看看我能想出什么。
答案 9 :(得分:0)
未经测试且未经证实:
我猜它不会那么简单......但它适用于你的例子,并不是真正重要的事情吗?