C# Euler项目:问题1(可能的重构和运行时优化)

C# Euler项目:问题1(可能的重构和运行时优化),c#,algorithm,optimization,refactoring,C#,Algorithm,Optimization,Refactoring,我听说了很多关于Euler项目的事情,所以我想我解决了C#中的一个问题。网站上所述的问题如下: class EulerProblem1 { public static void Main() { var totalNum = 1000; var counter = 1; var sum = 0; while (counter < totalNum)

我听说了很多关于Euler项目的事情,所以我想我解决了C#中的一个问题。网站上所述的问题如下:

  class EulerProblem1
    {
        public static void Main()
        {
            var totalNum = 1000;
            var counter = 1;
            var sum = 0;

            while (counter < totalNum)
            {
                if (DivisibleByThreeOrFive(counter))
                    sum += counter;

                counter++;
            }

            Console.WriteLine("Total Sum: {0}", sum);
            Console.ReadKey();
        }

        private static bool DivisibleByThreeOrFive(int counter)
        {
            return ((counter % 3 == 0) || (counter % 5 == 0));

        }
    } 
return ((counter % 3 == 0) || (counter % 5 == 0));
如果我们列出所有的自然数 低于10是3或5的倍数, 我们得到3,5,6和9。这些的总和 倍数是23

求3的所有倍数之和 或1000以下的5

我编写的代码如下:

  class EulerProblem1
    {
        public static void Main()
        {
            var totalNum = 1000;
            var counter = 1;
            var sum = 0;

            while (counter < totalNum)
            {
                if (DivisibleByThreeOrFive(counter))
                    sum += counter;

                counter++;
            }

            Console.WriteLine("Total Sum: {0}", sum);
            Console.ReadKey();
        }

        private static bool DivisibleByThreeOrFive(int counter)
        {
            return ((counter % 3 == 0) || (counter % 5 == 0));

        }
    } 
return ((counter % 3 == 0) || (counter % 5 == 0));
类EulerProblem1
{
公共静态void Main()
{
var totalNum=1000;
var计数器=1;
var总和=0;
while(计数器
如果能了解到一些关于使用更少冗长/更清晰的语法和更好的优化的替代实现的想法,那就太好了。想法可能会有所不同,从快速和肮脏到拿出大炮消灭蚊子。其目的是探索计算机科学的深度,同时尝试改进这个特别琐碎的代码片段


谢谢

这与我解决问题的方法基本相同。我知道在ProjectEuler论坛上还有其他解决方案(可能也更有效)


一旦你输入你的答案,回到这个问题,你就可以选择去论坛解决这个问题。你可能想看看那里

更新为不重复计数3和5的倍数:

int EulerProblem(int totalNum)
{
   int a = (totalNum-1)/3;
   int b = (totalNum-1)/5;
   int c = (totalNum-1)/15;
   int d = a*(a+1)/2;
   int e = b*(b+1)/2;
   int f = c*(c+1)/2;
   return 3*d + 5*e - 15*f;
}

如果您将其声明如下,则DivisibleBythree或Five中的代码将稍微快一些:

  class EulerProblem1
    {
        public static void Main()
        {
            var totalNum = 1000;
            var counter = 1;
            var sum = 0;

            while (counter < totalNum)
            {
                if (DivisibleByThreeOrFive(counter))
                    sum += counter;

                counter++;
            }

            Console.WriteLine("Total Sum: {0}", sum);
            Console.ReadKey();
        }

        private static bool DivisibleByThreeOrFive(int counter)
        {
            return ((counter % 3 == 0) || (counter % 5 == 0));

        }
    } 
return ((counter % 3 == 0) || (counter % 5 == 0));

如果您不想依靠编译器来内联函数调用,您可以自己将代码放入主例程中来实现这一点。

您可以为此提出一个封闭形式的解决方案。诀窍是寻找模式。尝试列出总结中的术语,比如10或20,然后用代数将它们分组。通过适当的替换,你可以把它推广到十以外的数字。只是要小心边缘情况。

这里是我原来的F#解到C#的音译。编辑:它基本上是姆贝基什作为循环而不是函数的解决方案(我去掉了重复计数)。我更喜欢姆贝基什

static int Euler1 ()
{
  int sum = 0;

  for (int i=3; i<1000; i+=3) sum+=i;
  for (int i=5; i<1000; i+=5) sum+=i;
  for (int i=15; i<1000; i+=15) sum-=i;

  return sum;
}
使用LINQ(根据评论中的建议进行更新)


我喜欢Technielogy的想法,这是我对修改的想法

static int Euler1 () 
{ 
  int sum = 0; 

  for (int i=3; i<1000; i+=3) 
  {
    if (i % 5 == 0) continue;
    sum+=i; 
  }
  for (int i=5; i<1000; i+=5) sum+=i; 

  return sum; 
} 
static int Euler1()
{ 
整数和=0;

对于(int i=3;i我已经有一段时间没有编写任何Java了,但这应该可以在固定时间内以很少的开销解决它:

public class EulerProblem1
{
    private static final int EULER1 = 233168;
    // Equal to the sum of all natural numbers less than 1000
    // which are multiples of 3 or 5, inclusive.

    public static void main(String[] args)
    {
        System.out.println(EULER1);
    }
}
编辑:这是一个C实现,如果每条指令都有意义:

#define STDOUT     1
#define OUT_LENGTH 8

int main (int argc, char **argv)
{
    const char out[OUT_LENGTH] = "233168\n";
    write(STDOUT, out, OUT_LENGTH);
}
注:

  • 调用
    write
    时没有错误处理。如果需要真正的健壮性,则必须采用更复杂的错误处理策略。增加的复杂性是否值得更高的可靠性取决于用户的需要
  • 如果您有内存限制,则可以使用直接字符数组而不是以多余的空字符结尾的字符串来保存一个字节。然而,实际上,
    out
    几乎肯定会被填充到8个字节
  • 尽管通过将字符串内联放置在
    write
    调用中可以避免
    out
    变量的声明,但任何真正的编译器都会优化声明
  • write
    syscall优先于
    put
    或类似方式使用,以避免额外的开销。理论上,您可以直接调用系统调用,可能会节省一些周期,但这会带来重大的可移植性问题。您的里程数可能会因这是否是可接受的折衷而有所不同

试试这个,在C语言中。这是一个常数时间,只有一个除法(如果编译器没有优化div/mod,应该是两个除法)。我确信可以让它更明显一点,但这应该是可行的

它基本上将总和分为两部分。较大的部分(对于N>=15)是一个简单的二次函数,它将N分为15个精确的块。较小的部分是不适合块的最后一位。后一位比较混乱,但只有很少的可能性,因此LUT将很快解决它

const unsigned long N = 1000 - 1;
const unsigned long q = N / 15;
const unsigned long r = N % 15;
const unsigned long rc = N - r;

unsigned long sum = ((q * 105 + 15) * q) >> 1;

switch (r) {
    case 3  : sum += 3  + 1*rc ; break;
    case 4  : sum += 3  + 1*rc ; break;
    case 5  : sum += 8  + 2*rc ; break;
    case 6  : sum += 14 + 3*rc ; break;
    case 7  : sum += 14 + 3*rc ; break;
    case 8  : sum += 14 + 3*rc ; break;
    case 9  : sum += 23 + 4*rc ; break;
    case 10 : sum += 33 + 5*rc ; break;
    case 11 : sum += 33 + 5*rc ; break;
    case 12 : sum += 45 + 6*rc ; break;
    case 13 : sum += 45 + 6*rc ; break;
    case 14 : sum += 45 + 6*rc ; break;
}

您可以这样做:

Func<int,int> Euler = total=> 
    new List<int>() {3,5}
        .Select(m => ((int) (total-1) / m) * m * (((int) (total-1) / m) + 1) / 2)
        .Aggregate( (T, m) => T+=m);
Func-Euler=total=>
新列表(){3,5}
。选择(m=>((int)(total-1)/m)*m*((int)(total-1)/m)+1)/2)
.骨料((T,m)=>T+=m);
你仍然有重复计算的问题。我会再考虑一下

编辑:

以下是LINQ中的一个有效解决方案(如果有点不雅观):

        var li = new List<int>() { 3, 5 };
        Func<int, int, int> Summation = (total, m) => 
           ((int) (total-1) / m) * m * (((int) (total-1) / m) + 1) / 2;

        Func<int,int> Euler = total=> 
            li
                .Select(m => Summation(total, m))
                .Aggregate((T, m) => T+=m)
            - Summation(total, li.Aggregate((T, m) => T*=m));
var li=newlist(){3,5};
Func求和=(总计,m)=>
((int)(total-1)/m)*m*((int)(total-1)/m)+1)/2;
Func Euler=total=>
锂
.选择(m=>总和(总计,m))
.骨料((T,m)=>T+=m)
-总和(总,li.骨料((T,m)=>T*=m));
你们中有谁能在这方面有所改进吗

说明:


记住线性级数的求和公式是n(n+1)/2。在第一种情况下,如果你有3,5<10的倍数,你需要求和(3+6+9,5)。设置total=10,你将整数序列设为1..(int)(total-1)/3,然后求和并乘以3。你可以很容易地看到我们只是设置了n=(int)(total-1)/3,然后使用求和公式并乘以3。一点代数为我们提供求和函子的公式。

重构@mbeckish非常聪明的解决方案:

public int eulerProblem(int max) {
    int t1 = f(max, 3);
    int t2 = f(max, 5);
    int t3 = f(max, 3 * 5);
    return t1 + t2 - t3;
}

private int f(int max, int n) {
    int a = (max - 1) / n;
    return n * a * (a + 1) / 2;
}
newlist{3,5}.SelectMany(n=>Enumerable.Range(1999/n).S
new List<int>{3,5}.SelectMany(n =>Enumerable.Range(1,999/n).Select(i=>i*n))
                  .Distinct()
                  .Sum()