Project Euler: Problem 1 (Possible refactorings and run time optimizations)

前端 未结 13 2042
半阙折子戏
半阙折子戏 2020-12-11 08:35

I have been hearing a lot about Project Euler so I thought I solve one of the problems in C#. The problem as stated on the website is as follows:

If w

相关标签:
13条回答
  • 2020-12-11 08:54

    The code in DivisibleByThreeOrFive would be slightly faster if you would state it as follows:

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

    And if you do not want to rely on the compiler to inline the function call, you could do this yourself by putting this code into the Main routine.

    0 讨论(0)
  • 2020-12-11 08:57

    With LINQ (updated as suggested in comments)

    static void Main(string[] args)
    {
        var total = Enumerable.Range(0,1000)
                        .Where(counter => (counter%3 == 0) || (counter%5 == 0))
                        .Sum();
    
        Console.WriteLine(total);
        Console.ReadKey();
    }
    
    0 讨论(0)
  • 2020-12-11 08:57

    I like technielogys idea, here's my idea of a modification

    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; 
    } 
    

    Though also comes to mind is maybe a minor heuristic, does this make any improvement?

    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<250; i+=5)
      {
        sum+=i;
      }
      for (int i=250; i<500; i+=5)
      {
        sum+=i;
        sum+=i*2;
        sum+=(i*2)+5;
      }
    
      return sum; 
    } 
    
    0 讨论(0)
  • 2020-12-11 08:59

    You can do something like this:

    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);
    

    You still have the double counting problem. I'll think about this a little more.

    Edit:

    Here is a working (if slightly inelegant) solution in 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));
    

    Can any of you guys improve on this?

    Explanation:

    Remember the summation formula for a linear progression is n(n+1)/2. In the first case where you have multiples of 3,5 < 10, you want Sum(3+6+9,5). Setting total=10, you make a sequence of the integers 1 .. (int) (total-1)/3, and then sum the sequence and multiply by 3. You can easily see that we're just setting n=(int) (total-1)/3, then using the summation formula and multiplying by 3. A little algebra gives us the formula for the Summation functor.

    0 讨论(0)
  • 2020-12-11 09:01

    I haven't written any Java in a while, but this should solve it in constant time with little overhead:

    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);
        }
    }
    

    EDIT: Here's a C implementation, if every instruction counts:

    #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);
    }
    

    Notes:

    • There's no error handling on the call to write. If true robustness is needed, a more sophisticated error handling strategy must be employed. Whether the added complexity is worth greater reliability depends on the needs of the user.
    • If you have memory constraints, you may be able to save a byte by using a straight char array rather than a string terminated by a superfluous null character. In practice, however, out would almost certainly be padded to 8 bytes anyway.
    • Although the declaration of the out variable could be avoided by placing the string inline in the write call, any real compiler willoptimize away the declaration.
    • The write syscall is used in preference to puts or similar to avoid the additional overhead. Theoretically, you could invoke the system call directly, perhaps saving a few cycles, but this would raise significant portability issues. Your mileage may vary regarding whether this is an acceptable tradeoff.
    0 讨论(0)
  • 2020-12-11 09:02

    Here's a transliteration of my original F# solution into C#. Edited: It's basically mbeckish's solution as a loop rather than a function (and I remove the double count). I like mbeckish's better.

    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;
    }
    

    Here's the original:

    let euler1 d0 d1 n =
      (seq {d0..d0..n}       |> Seq.sum) +
      (seq {d1..d1..n}       |> Seq.sum) -
      (seq {d0*d1..d0*d1..n} |> Seq.sum)
    
    let result = euler1 3 5 (1000-1)
    
    0 讨论(0)
提交回复
热议问题