假设您有一百万个连续的整数。 返回a,b和c的所有可能值,以便
a+b+c<=d.
d will be provided to you.
ex: if the numbers are 1,2,3,4,5,6,7
and d=7
[1,2,3]
[1,2,4]
[1,2,3] will be same as [1,3,2] and [3,2,1]...
因为一百万太大了,在这个例子中我以1000为例。同样为了方便起见,我使用1到1000作为数据集。 假设
`a<b<c`
因此3*a<1000
==&gt; a&lt; 333.33,所以我从1到333提取a
。
static void Main(string[] args)
{
int d = 519;
var setA = Enumerable.Range(1, 333);
IEnumerable<int> value = Enumerable.Range(1, 1000);
var result = (from a in setA
from b in value
from c in value
where a!=b && a!=c && b!=c && a + b + c <= d
select new {a,b,c}).ToList().Distinct().ToList();
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
速度慢,抛出System.OutOfMemoryException
例外.....
答案 0 :(得分:1)
将a
缩小到1...333
是一个好主意。您可以使用a < b < c
这一事实进一步改进代码,因此b,c in Enumerable.Range(1, 1000)
不是最理想的。
您可以分别根据给定的数字b
和c
定义a
和b
的下限和上限:
a < b => b >= a + 1, b in Enumerable.Range(a + 1, ...)
b < c => c must be in Enumerable.Range(b + 1, ...)
此外,您可以像这样定义a
和b
的界限:
b >= a + 1
和a + b + c <= total
,a + (a + 1) + ((a + 1) + 1) <= total
也必须成立。也就是说,a < total / 3
是不够的。它是a <= (total - 3) / 3
a + b + (b + 1) <= total
,即b <= (total - a - 1) / 2
a + b + c <= total
会转换为c <= total - a - b
您可以通过嵌套迭代并使用SelectMany
展平结果列表来利用它:
var result = Enumerable.Range(1, (total - dba - dca) / 3)
.SelectMany(
a => Enumerable.Range(a + 1, (total - a - dcb) / 2 - a)
.SelectMany(
b => Enumerable.Range(b + 1, (total - a - b) - b)
.Select(c => new { a, b, c })));
至于你的表现和内存不足问题:
从LINQ查询中删除ToList()
。它会导致所有结果在开始处理之前加载到内存中。由于您只想打印元组,因此不需要将所有元素加载到内存中。这是LINQ的最大优势 - 它只返回一个枚举器而不实际计算结果。如果从LINQ查询中删除ToList()
,for-each循环将计算每次迭代的一个结果,将其打印出来,然后再次忘记它。
作为对您的评论的解释性答案:
Enumerable.Range
的实现如下:
private static IEnumerable<int> RangeIterator(int start, int count)
{
for (int i = 0; i < count; ++i)
yield return start + i;
}
SelectMany
的实施:
private static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{
foreach (TSource source1 in source)
{
foreach (TResult result in selector(source1))
yield return result;
}
}
例如,
foreach (var item in Enumerable.Range(1, 10).SelectMany(n => Enumerable.Range(1, n)))
{ /* do something */ }
概念上转换为:
for (int i = 0; i < 10; ++i)
{
var n = 1 + i;
for (int j = 0; j < n; ++j)
{
var result = 1 + j;
/* do something */ // <- processes the current result and forgets it again
}
}
但是,当您添加ToList
时:
foreach (var item in Enumerable.Range(1, 10).SelectMany(n => Enumerable.Range(1, n)).ToList())
{ /* do something */ }
这转化为:
var list = new List<int>();
for (int i = 0; i < 10; ++i)
{
var n = 1 + i;
for (int j = 0; j < n; ++j)
{
var item = 1 + j;
list.Add(item); // <- puts the current result in a list
}
}
// list content: 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6...
foreach (var item in list)
{ /* do something */ }
答案 1 :(得分:0)
假设输入已排序,您可以使用index
和每个for
循环的选择进行播放。第一个应该是主数组的三分之一,第二个循环应该不超过一半,但是从第一个循环的索引开始。像这样的东西:
const int totalElements = 1000;
const int target = 1000;
var value = Enumerable.Range(1, totalElements).OrderBy(dd => dd).ToList();
var sw = new Stopwatch();
var result = new List<int[]>();
sw.Start();
for (int i = 0; i < value.Count / 3; i++)
{
var a = value[i];
for (int j = i + 1; j < value.Count / 2; j++)
{
var b = value[j];
for (int k = j + 1; k < value.Count; k++)
{
var c = value[k];
if (a + b + c <= target)
{
var newR = new[] { a, b, c };
result.Add(newR);
}
}
}
}
sw.Stop();
Console.WriteLine("Time Taken: " + sw.Elapsed.TotalSeconds);
Console.WriteLine("Total result count: " + result2.Count);
对于1000,它在我糟糕的机器上需要4.9秒,而LINQ正在投掷outofmemory exception
。最重要的是你必须在LINQ中添加某种相等比较,否则你会得到像1 + 2 + 3和3 + 2 + 1这样的结果,并且只使用索引就可以摆脱它。
您还可以通过执行以下操作来调整范围:
for (int i = 0; i < value.Count / 3; i++)
{
var a = value[i];
var index = value.IndexOf(target - a) + 1;
for (int j = i + 1; j < index; j++)
{
var b = value[j];
var remainingIndex = value.IndexOf(target - (a + b)) + 1;
for (int k = j + 1; k < remainingIndex; k++)
{
var c = value[k];
if (a + b + c <= target)
{
var newR = new[] { a, b, c };
result.Add(newR);
}
}
}
}
如果值像这个例子一样是顺序的,你可以更容易地找到索引。如果不是这种情况,你必须通过Binary Search
找到索引,但无论如何它可能都是类似的表现。
答案 2 :(得分:0)
我建议将数字存储在SortedSet类中。您可以执行以下操作(伪代码):
For each n in GetViewBetween (1, d)
For each m in GetViewBetween (n+1, d-n)
For each k in GetViewBetween (m+1, d-n-m)
Store or print or callback or return with continuation the Tuple object with n, m, k
Loop
Loop
Loop
答案 3 :(得分:-1)
我会建议你一个几何表示:想象一下,你的&#39; d&#39;是三角形边界的总和。好吧,你需要找到适合的所有三角形。在您的代码中,您可以使用双变量:(a + b + c)=(b + c + a)。但是如果你为每个三角形边做3个循环,那么它会更快。