我听过很多关于Project Euler的消息,所以我想我解决了C#中的一个问题。网站上说明的问题如下:
如果我们列出所有自然数字 10以下是3或5的倍数, 我们得到3,5,6和9.这些的总和 倍数是23。
求出3的所有倍数之和 或者低于1000。
我编写了如下代码:
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));
}
}
以更少的冗长/更清晰的语法和更好的优化来获得关于替代实现的一些想法会很棒。这些想法可能从快速和肮脏到带出大炮消灭蚊子。目的是探索计算机科学的深度,同时尝试改进这个特别简单的代码片段。
由于
答案 0 :(得分:9)
更新为不重复计算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;
}
答案 1 :(得分:4)
这是我原来的F#解决方案到C#的音译。编辑:它基本上是mbeckish的解决方案,作为循环而不是函数(我删除了双重计数)。我喜欢mbeckish更好。
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;
}
这是原作:
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)
答案 2 :(得分:4)
使用LINQ(根据评论中的建议更新)
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();
}
答案 3 :(得分:3)
我有一段时间没有写过任何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个字节。out
调用中将字符串内联放置来避免声明write
变量,但任何真正的编译器都会优化声明。write
系统调用优先于puts
或类似使用,以避免额外开销。从理论上讲,您可以直接调用系统调用,可能会节省几个周期,但这会引发重大的可移植性问题。关于这是否是可接受的权衡,您的里程可能会有所不同。答案 4 :(得分: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;
}
答案 5 :(得分:1)
这与我解决问题的方式基本相同。我知道在project-euler的论坛上还有其他解决方案(可能也更有效)。
一旦你输入你的答案回到问题,你就可以选择去论坛解决这个问题。你可能想看看那里!
答案 6 :(得分:1)
如果您按如下方式说明,DivisibleByThreeOrFive中的代码会稍快一些:
return ((counter % 3 == 0) || (counter % 5 == 0));
如果您不想依赖编译器来内联函数调用,您可以将此代码放入Main例程中来自行完成。
答案 7 :(得分:1)
您可以为此提出一个封闭的表单解决方案。诀窍是寻找模式。尝试列出总计最多10个或20个的术语,然后使用代数对它们进行分组。通过进行适当的替换,您可以将其推广到十个以外的数字。请注意边缘情况。
答案 8 :(得分:1)
在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;
}
答案 9 :(得分:1)
您可以这样做:
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);
你仍然有重复计算问题。我会再考虑一下这个。
修改强>
以下是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));
你们中的任何人都可以改进吗?
<强>解释强>
记住线性进展的求和公式是n(n + 1)/ 2。在第一种情况下,你有3,5的倍数&lt; 10,你想要Sum(3 + 6 + 9,5)。设置total = 10,你创建一个整数序列1 ..(int)(total-1)/ 3,然后对序列求和并乘以3.你可以很容易地看到我们只是设置n =(int )(total-1)/ 3,然后使用求和公式并乘以3.一个小代数给出了求和函子的公式。
答案 10 :(得分:0)
我喜欢technielogys的想法,这是我对修改的想法
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 ()
{
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;
}
答案 11 :(得分:0)
Your approach is brute force apprach, The time complexity of the following approach is O(1), Here we
are dividing the given (number-1) by 3, 5 and 15, and store in countNumOf3,countNumOf5, countNumOf15.
Now we can say that 3 will make AP, within the range of given (number-1) with difference of 3.
suppose you are given number is 16, then
3=> 3, 6, 9, 12, 15= sum1=>45
5=> 5, 10, 15 sum2=> 30
15=> 15 => sum3=15
Add sum= sum1 and sum2
Here 15 is multiple of 3 and 5 so remove sum3 form sum, this will be your answer. **sum=sum-
sum3** please check link of my solution on http://ideone.com/beXsam]
import java.util.*;
class Multiplesof3And5 {
public static void main(String [] args){
Scanner scan=new Scanner(System.in);
int num=scan.nextInt();
System.out.println(getSum(num));
}
public static long getSum(int n){
int countNumOf3=(n-1)/3;//
int countNumOf5=(n-1)/5;
int countNumOf15=(n-1)/15;
long sum=0;
sum=sumOfAP(3,countNumOf3,3)+sumOfAP(5,countNumOf5,5)-sumOfAP(15,countNumOf15,15);
return sum;
}
public static int sumOfAP(int a, int n, int d){
return (n*(2*a +(n -1)*d))/2;
}
}
答案 12 :(得分:0)
new List<int>{3,5}.SelectMany(n =>Enumerable.Range(1,999/n).Select(i=>i*n))
.Distinct()
.Sum()
[更新](回应要求解释此algorothm的评论) 这为每个基值(在这种情况下为3和5)构建了一个扁平的倍数列表,然后删除重复(例如,在这种情况下,乘数可以被3 * 5 = 15除数),然后对剩余值求和。 (与我在这里看到的任何其他解决方案相比,这可以很容易地推广到具有两个以上的基本值IMHO。)