繁体   English   中英

3或5的倍数升序

[英]Multiple of 3 or 5 in ascending order

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

但是练习的主要目的是纠正最快的算法。

我在第一种算法中缺少什么还是应该保留第二种算法?

这是我的实现,尽管按照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();

    }

对于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 => {})
136ms,51266ms,273409ms, 80%

Mrinal Kamboj-不带AsParallel
154ms,14559ms,1985ms,13%

MHD-第二个解决方案
69ms,5791ms,879ms,13%

ThomW-添加条件以防止重复
34ms,2398ms,521ms,13%

Logman-带有3,5的预先计算的增量作为原始答案
43ms,3498ms,654ms,13%

Logman-通用解决方案
47ms,3529ms,1270ms,13%

Logman + HABO-带有3,5的预先计算的增量
37ms,2655ms,501ms,13%

Logman + HABO-通用解决方案
37ms,2701ms,1149ms,13%

HABO-展开的解决方案(请参阅评论)
32ms,2072ms,464ms,13%

测试码

我想以最快的方式获得低于给定数字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]; }

最快的算法几乎总是预先计算的查找表 提前解决问题并保存结果; 您的程序然后只查找预先计算的结果。 它通常不会比这更快。 怎么可能

现在很明显,您实际上不是在寻找最快的算法吗? 实际上在寻找什么算法?

查看结果,值似乎每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是由分支预测引起的,因此,在进行这些难题时,尽可能减少难题总是一个好主意)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM