在C#中,我有一组独特的元素,我想为每个无序对执行一些代码。例如,如果我的容器包含{a,b,c},则无序对是(a,b),(a,c)和(b,c)。问题出现在执行2-opt优化的范围内,因此效率是个问题。
我目前的解决方案如下:
foreach(var a in container)
{
foreach(var b in container)
{
if (a < b)
{
// execute code
}
}
}
显然,如果operator []可用于获取第i个元素(即如果底层数据结构是列表),则可以轻松修改。但对于所有其他容器,解决方案取决于是否存在一些比较功能并不是很有效。
我还尝试了一种基于LINQ语句的公式,它只生成一对所需的对。然而,正如预期的那样,这比第一种方法慢得多。这也适用于使用ElementAt
的解决方案。
编辑:这是使用的(改进的)LINQ代码:
var x = from a in container
from b in container
where a < b
select new KeyValuePair<int,int>(a,b);
与其他解决方案相比,执行速度慢了3-5倍。
这是我在C ++中实现它的方式(获得良好的效率):
for(auto it1 = container.begin(); it1!=container.end(); ++it1)
{
auto it2 = it1;
for(++it2; it2!=container.end(); ++it2)
{
// execute code
}
}
不幸的是,要将其转换为C#,需要克隆(内部使用的)枚举器,语言本身不支持。
有没有人有更好的主意/解决方案?
答案 0 :(得分:1)
您是否尝试先将元素复制到列表中,然后使用索引器([i]
)运算符执行算法?由于该算法具有二次运行时间,因此在其前面进行线性复制操作可能是微不足道的。你必须自己找出小型,中型和大型容器的实际运行时间......
我认为值得一试,这可能比每次使用比较运算符快得多。
您还可以检查容器是否为IList<T>
类型并跳过复制操作。
答案 1 :(得分:1)
如果您不关心订单,可以这样做:
int i = 0;
foreach (var a in list)
{
int j = 0;
foreach (var b in list)
{
if (i <= j)
break;
// execute code
j++;
}
i++;
}
如果您关心订单,则可以将自己限制为实施IList<T>
的集合,其中包含[]
运算符。或者您可以先将集合复制到List<T>
,然后再使用它。
答案 2 :(得分:0)
C#中的枚举器与您问题中的C ++枚举器不同。在C#中,您既没有容器的begin
也没有end
元素。您只有Current
元素和Next()
方法。它允许你产生更多的序列,例如 - 你可以通过无穷大的随机数序列进行枚举,这显然没有开始或结束。
所以 - 你不能只在C ++代码中使用IEnumerable
类在C#中执行此操作。最好的方法是使用System.Collection.Generics.IList<T>
接口。许多类型(例如数组)都继承了这个接口。
如果你使用IEnumerable
,那么在你的类型中你(在大多数情况下)迭代一些集合。如果你这样做 - 你可以实现IList<T>
接口。
还有另一种解决方案 - 在C#列表中,引用类型数组仅包含对象的引用。所以 - 您可以将数据复制到本地列表并使用它。但这取决于你的记忆力和性能要求。
答案 3 :(得分:0)
将您的项目放入List或IList中,然后您可以使用与C ++代码非常相似的索引访问它们。
for (int i = 0; i < container.Count(); i++)
for (int j = i+1; j < container.Count(); j++)
{
var item1 = container.Item[i];
var item2 = container.Item[j];
}
我希望使用索引而不是n ^ 2比较迭代有序集合会更有效。比较运算符的效率很重要,但您根本不需要进行比较。