我希望以最快的方式获得给定数字下方3或5的所有数字的倍数。 所以我做了以下代码:
double limit = number / 5;
for (var i = 0; i <= number / 3; i++)
{
list.Add(i * 3);
if (i <= limit && ((i * 5) % 3 != 0))
{
list.Add(i * 5);
}
}
但结果列表不会按升序排序。
如果number = 11
,则列表包含
0,3,5,6,10,9
而不是
0,3,5,6,9,10
要获得排序列表,我可以使用简单的迭代,如:
for (var i = 0; i <= number; i++)
{
if (i % 3 == 0 || i % 5 == 0)
{
list.Add(i);
}
}
但练习的主要目的是确保最快的算法。
我在第一个算法中遗漏了什么,或者我应该保留第二个算法?
答案 0 :(得分:3)
这是我的实现,尽管它可能不是BigO表示法中最快的。
static void Main(string[] args)
{
int number = 35;
List<int> list = new List<int>();
int count3 = 0;
int count5 = 0;
int step3 = 3;
int step5 = 5;
while (count3 < number || count5<number )
{
if ((count3 + step3) <= (count5 + step5))
{
count3 += step3;
if (count3 <= number)
{
list.Add(count3);
}
}
else
{
count5 += step5;
if (count5 <= number)
{
list.Add(count5);
}
}
}
foreach (var l in list)
{
Console.WriteLine(l.ToString());
}
Console.ReadLine();
}
答案 1 :(得分:2)
对于3和5,每15(3 * 5 = 15)存在循环依赖,这就是为什么我们可以做这样的事情:
static void Main()
{
int[] deltas= { 3,2,1,3,1,2,3 };
int number = 30;
List<int> result = new List<int>();
int j = 1;
for(int i = deltas[0]; i<=number; i+=deltas[j++%deltas.Length])
{
result.Add(i);
}
foreach(int i in result)
Console.Write(i+", ");
}
<强>更新强>
要找到圆点,我们需要计算最小公倍数。现在要找到所有这些点的增量,我们只需要以不太理想的方式解决原始问题并相互减去元素。
更通用的版本:
static void Main()
{
foreach(int i in multi(30, new []{2,3,4,5,7} ))
Console.Write(i+", ");
}
static List<int> multi(int max, int[] divs)
{
int[] deltas = calcDeltas(divs);
List<int> result = new List<int>();
int j = 1;
for(int i = deltas[0]; i<=max; i+=deltas[j++%deltas.Length])
{
result.Add(i);
}
return result;
}
static int[] calcDeltas(int[] divs)
{
long max = 1;
foreach(int div in divs)
max = lcm(max,div);
List<long> ret = new List<long>();
foreach(int div in divs)
{
for(int i=div; i<=max; i+=div)
{
int idx = ret.BinarySearch(i);
if (idx < 0)
{
ret.Insert(~idx, i);
}
}
}
for(int i=ret.Count-1; i>0; i--)
ret[i]-=ret[i-1];
return ret.ConvertAll(x => (int)x).ToArray();
}
static long gcf(long a, long b)
{
while (b != 0)
{
long temp = b;
b = a % b;
a = temp;
}
return a;
}
static long lcm(long a, long b)
{
return (a / gcf(a, b)) * b;
}
更新2:
我为提供的解决方案做了一些测试(没有Eric的解决方案)。我修改了一些函数以得到完全相同的结果(更多描述)。在i7-3770K@3.5GH上测试。
用户名 - 说明
时间1 - 平均时间,循环= 100,数字= 10000000
时间2 - 总时间,循环= 100000,数字= 10000
时间3 - 总时间,循环= 1000000,数字= 100
CPU使用率 - 百分比
Mrinal Kamboj - 建议OrderBy保持正确的订单 434ms,85447ms,348600ms, 70%
Mrinal Kamboj - 建议使用OrderBy保留正确的顺序,而不使用ToList()来实现查询完成ForAll(x =&gt; {})
136毫秒,51266毫秒,273409毫秒, 80%
Mrinal Kamboj - 没有AsParallel
154毫秒,14559毫秒,1985毫秒,13%
Mhd - 第二个解决方案
69毫秒,5791毫秒,879毫秒,13%
ThomW - 增加了防止重复的条件
34ms,2398ms,521ms,13%
Logman - 预先计算的增量为3,5作为原始答案
43毫秒,3498毫秒,654毫秒,13%
Logman - 通用解决方案
47毫秒,3529毫秒,1270毫秒,13%
Logman + HABO - 具有3,5的预先计算的增量 37毫秒,2655毫秒,501毫秒,13%
Logman + HABO - 通用解决方案
37毫秒,2701毫秒,1149毫秒,13%
HABO - 展开的解决方案(见评论)
32ms,2072ms,464ms,13%
答案 2 :(得分:1)
我想以最快的方式获得给定数字以下3或5的所有数字的倍数。
轻松完成。
private static readonly int [][] lookup = {
{}, // 0
{0}, // 1
{0}, // 2
{0}, // 3
{0, 3}, // 4
{0, 3}, // 5
{0, 3, 5}, // 6
{0, 3, 5, 6}, // 7
{0, 3, 5, 6}, // 8
{0, 3, 5, 6}, // 9
{0, 3, 5, 6, 9}, // 10
... and so on; fill out the table as far as you like
};
现在你的算法是:
static int[] Blah(int x) { return lookup[x]; }
最快的算法几乎总是预先计算的查找表。提前解决问题并保存结果;然后你的程序只查找预先计算的结果。它通常不会比这更快;怎么可能呢?
现在很明显,您实际上并没有寻找最快的算法?您实际正在寻找什么算法?
答案 3 :(得分:0)
查看结果,值似乎每14个项目包含一个重复模式,这可能是找到最佳算法的关键(缺少扩展查找表)
可能总是可以进行更多优化(例如预先计算预期的大小),但是以下内容可以实现预期结果并且应该表现良好:
var list=new List<int>();
int[] seq = {0,3,5,6,9,10,12,15,18,20,21,24,25,27};
for(int i = 0;;){
int val = seq[i % 14] + (i++/14) * 30;
if(val > number)break;
list.Add(val);
}
(旁注:一般来说,一个难以衡量的因素是分支预测if
的原因,因此在做这些谜题时,尽可能少地获取这些谜题总是一个好主意。