3或5的倍数按升序排列

时间:2017-04-28 19:59:29

标签: c# algorithm time-complexity

我希望以最快的方式获得给定数字下方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);
    }
}

但练习的主要目的是确保最快的算法。

我在第一个算法中遗漏了什么,或者我应该保留第二个算法?

4 个答案:

答案 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%

Test code

答案 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的原因,因此在做这些谜题时,尽可能少地获取这些谜题总是一个好主意。