我有一个+ ve和-ve值列表,我需要:
e.g。
input = 1.00,2.92,-2.92,3.00,7.56,-7.56,8.00, -100.93, -40.56 ......
结束目标:
listA = 1, 3, 8 listB = -4056, -10093
我正在寻找改进我的下面代码的建议,速度和准确性都很重要。
var results = new List<decimal>(input.Select(x => x*100 ));
results.Sort();
var listA = results.FindAll(x => ((decimal)x > 0));
var listB = results.FindAll(x => ((decimal)x < 0));
decimal[] FromA_NotIn_B = listA.Except(listB.Select(X => (X = X * -1))).ToArray();
decimal[] FromB_NotIn_A = listB.Except(listA.Select(X => (X = X * -1))).ToArray();
答案 0 :(得分:0)
您的代码在准确性方面是正确的(十进制在反转其符号时不会失去精度)。通过使用循环编写并执行手动完全外部合并连接,其速度可以确定地提高20倍。但是,如果性能很重要,我只会推荐这个,因为这会使代码大小爆炸5倍。
编辑:合并连接意味着并排获取两个已排序的列表,并以可以找到相等元素的方式沿着两个列表行走,即使两个数组中都有多余的元素。这是merge-sort中合并步骤的扩展。 Full-outer意味着它在数据库中的含义:保留仅在其中一个数组中的值。
唯一的要求是排序列表,因为您对结果列表进行排序。
您的代码可以这样工作:
var results = new List<decimal>(input.Select(x => x*100 ));
results.Sort();
var listA = new List<decimal>();
var listB = new List<decimal>();
//now to the merge-join and fill listA and B
合并连接实现起来很复杂,但是为了让你在这里开始是一个基于迭代器的版本,我在我的项目中使用两个巨大的文件之间的连接,我永远无法将其加载到内存中但是已经排序。
public static IEnumerable<TResult> MergeJoin_OneToOne<TLeft, TRight, TResult>(
IEnumerable<TLeft> leftCollection,
IEnumerable<TRight> rightCollection,
Func<TLeft, TRight, int> comparison,
Func<TLeft, TResult> onlyLeftSelector,
Func<TRight, TResult> onlyRightSelector,
Func<TLeft, TRight, TResult> bothSelector)
{
return MergeJoin_OneToOne_Impl(leftCollection, rightCollection, comparison, onlyLeftSelector, onlyRightSelector, bothSelector);
}
static IEnumerable<TResult> MergeJoin_OneToOne_Impl<TLeft, TRight, TResult>(
IEnumerable<TLeft> leftCollection,
IEnumerable<TRight> rightCollection,
Func<TLeft, TRight, int> comparison,
Func<TLeft, TResult> onlyLeftSelector,
Func<TRight, TResult> onlyRightSelector,
Func<TLeft, TRight, TResult> bothSelector)
{
if (leftCollection == null) throw new ArgumentNullException("leftCollection");
if (rightCollection == null) throw new ArgumentNullException("rightCollection");
if (comparison == null) throw new ArgumentNullException("comparison");
if (onlyLeftSelector == null) throw new ArgumentNullException("onlyLeftSelector");
if (onlyRightSelector == null) throw new ArgumentNullException("onlyRightSelector");
if (bothSelector == null) throw new ArgumentNullException("bothSelector");
using (var leftEnum = leftCollection.GetEnumerator())
using (var rightEnum = rightCollection.GetEnumerator())
{
if (!leftEnum.MoveNext())
{
while (rightEnum.MoveNext()) yield return onlyRightSelector(rightEnum.Current);
yield break;
}
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
while (true)
{
int cmp = comparison(leftEnum.Current, rightEnum.Current);
if (cmp == 0)
{
yield return bothSelector(leftEnum.Current, rightEnum.Current);
if (!leftEnum.MoveNext())
{
while (rightEnum.MoveNext())
{
yield return onlyRightSelector(rightEnum.Current);
}
yield break;
}
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
}
else if (cmp < 0)
{
yield return onlyLeftSelector(leftEnum.Current);
if (!leftEnum.MoveNext())
{
do
{
yield return onlyRightSelector(rightEnum.Current);
} while (rightEnum.MoveNext());
yield break;
}
}
else
{
yield return onlyRightSelector(rightEnum.Current);
if (!rightEnum.MoveNext())
{
do
{
yield return onlyLeftSelector(leftEnum.Current);
} while (leftEnum.MoveNext());
yield break;
}
}
}
}
}
出于性能原因,您将此功能专门用于IList<decimal>
个元素。踢掉迭代器的东西并使用两个整数来表示列表中的当前位置。在行走列表A中忽略lt 0的元素和B的相同元素。
速度提升将来自多项改进:对象分配大大减少;由Except执行的散列操作已经消失;没有lambda,没有间接。
答案 1 :(得分:0)
作品:
static void Main(string[] args)
{
double[] input = { 1.00, 2.92, -2.92, 3.00, 7.56, -7.56, 8.00, -100.93, -40.56 };
IEnumerable<int> intsWithouDuplicates = input.Where(d => !input.Contains(-d)).Select(d => (int)(d * 100)).OrderBy(i => i);
var listA = intsWithouDuplicates.Where(i => i >= 0);
var listB = intsWithouDuplicates.Where(i => i < 0);
listA.ToList().ForEach(Console.WriteLine);
Console.WriteLine();
listB.ToList().ForEach(Console.WriteLine);
Console.ReadKey();
}
答案 2 :(得分:0)
这应该更快,并可靠地删除重复 我跳过了“乘以100”的要求,因为它对概念没有贡献:
1)按ABS值排序
2)与堆栈顶部比较,如果匹配则弹出,否则按
3)在过程结束时,堆栈仅包含没有匹配的元素
decimal[] input = new decimal[] { -6, 1, 2, 3, -1, 2, 2, -2, 6, 7, -4, 4, -7, 8, -4 };
Stack<decimal> st = new Stack<decimal>();
IEnumerable<decimal> sorted = input.OrderBy(X => X > 0 ? X : -X);
foreach (decimal item in sorted)
if (st.Any() && st.Peek() == -item)
st.Pop();
else
st.Push(item);
decimal[] pos = st.Where(X => X >= 0).ToArray();
decimal[] neg = st.Where(X => X < 0).ToArray();