1
\$\begingroup\$

I'm new to JavaScript, and decided to learn the language by running through as many of the Project Euler problems as I have time to do. I just finished the first one, and am looking for some feedback, specifically on language specific features I could use to do this faster, and faster performance overall (it seems to run a little slow) after some edits to fit the problem, it runs much faster, the document.write seemed to be really slowing the code down (probably from directly editing the page once every division of 3 or 5), and writing once value makes a huge difference.

Working fiddle here.

document.write = function (val) {
    document.body.insertAdjacentHTML("beforeend", val);
}

//If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

//Find the sum of all the multiples of 3 or 5 below 1000.

var ans = [];
var k = 0;

for (var i = 1; i < 1000; i++) {
    if (i % 3 === 0 || i % 5 === 0) {
        ans[k] = i;
        k++;
    }
}

var sum = 0;

for (var i = 0; i < ans.length; i++) {
    sum += ans[i];
}

document.write(sum);

//If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

//Find the sum of all the multiples of 3 or 5 below 1000.

var ans = [];
var k = 0;

for (var i = 1; i < 1000; i++) {
    if (i % 3 === 0 || i % 5 === 0) {
        ans[k] = i;
        k++;
    }
}

var sum = 0;

for (var i = 0; i < ans.length; i++) {
    sum += ans[i];
}

document.write(sum);
\$\endgroup\$
1
  • 2
    \$\begingroup\$ There are already some questions about solving PE #1 in other languages, so they don't count as duplicates. But some of the suggested improvements in the answers are in fact language independent (in particular the "performance boosting" ones), and could applied here as well, so I would recommend to have a look at them. \$\endgroup\$
    – Martin R
    Commented Jan 8, 2015 at 20:55

4 Answers 4

4
\$\begingroup\$

You missed the specification.

Find the sum of all the multiples of 3 or 5 below 1000.

You print all of the multiples of 3 or 5 below 1000. To meet the spec, you need to actually sum up all of the elements of ans.

\$\endgroup\$
0
3
\$\begingroup\$
ans[k] = i;
k++;

This is unnecessary and somewhat slower than using .push(). I would advise removing the k variable and instead using ans.push(i);. You're also currently looping once to accumulate all the multiples, then again to calculate the sum. Instead, try this:

//If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

//Find the sum of all the multiples of 3 or 5 below 1000.

var ans = [];
var sum = 0;

for (var i = 1; i < 1000; i++) {
    if (i % 3 === 0 || i % 5 === 0) {
        ans.push(i);
        sum += i;
    }
}

document.write(sum);
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Or I could just sum all the answer values instead of saving them to an array, assuming I only need to display a sum value. \$\endgroup\$ Commented Jan 12, 2015 at 14:23
3
\$\begingroup\$

You can use Array.reduce for the sum of an array, calculate the sum directly in a loop, or create a function using the formula of the sum of a arithmetic series. The latter should give the best performance I suppose:

// use an array
var arr = [], res = document.querySelector('#result');
for (var i=1; i<1000; i+=1) {
  if (i%3==0 || i%5==0) { arr.push(i); }
}

res.innerHTML = 'array arr (first/last) =&gt; [' + arr[0]+' .. '+arr[arr.length-1] + ']';
res.innerHTML += '<br>sum of multiples of 3 or 5 for 1-1000 via array: '+
  arr.reduce( function(a, b) {return a + b;}, 0);

// calculate sum directly
var sum = 0, i=1000;
while (i--) {
  sum += (i%3==0 || i%5==0) ? i : 0;
}
res.innerHTML += '<br>sum of multiples of 3 or 5 for 1-1000 direct calculation: '+ sum;

// function using sum of arithmetic series
function multiplesOf(ofn, max) {
  ofn = ofn instanceof Array ? ofn : [ofn];
  var i = max-1 || 999;
  var terms = ofn.map(function (v) {
               var v_ = Math.floor(i/v); 
               return ( ( v * (v_*(v_+1)) ) / 2  )  ;
              });
  // exclude combinations (e.g. 3 and 5 => all i/15)
  var combine = ofn.reduce( function (a, b) {return a * b;}, 1);
  var ncombine = Math.floor(i/combine);
  terms.push(
     ncombine
      ? -(( combine * ( ncombine * ( ncombine+1 ) ) ) / 2)
      : combine/2 < i
        ? -(combine / 2)
        : 0 );
  
  return Math.abs( ofn.length == 1
                   ? terms[1]
                   : terms.reduce(function (a,b,i,arr) {
                        return a + b;
                     }, 0) );
}
res.innerHTML += '<br>sum of multiples of 3 or 5 for 1-1000 formula: '+ 
                 multiplesOf([3,5], 1000);
<div id="result"></div>

\$\endgroup\$
2
\$\begingroup\$

You are going through all the numbers and checking if they are dividible by 3 or 5, a long winded way.

Instead create a function:

function sum_of_all_multiples(d,max_) {
    // Return the sum of all the numbers that are multiples
    // of d by actually incrementing by d each step
}

max_ = 1000
alert(sum_of_all_multiples(3,max_)+sum_of_all_multiples(5,max_)-sum_of_all_multiples(15,max_))
\$\endgroup\$
1
  • \$\begingroup\$ This is a really good answer, doing it this way cuts down the number of iterations in the loop to 600 from 1000. Great suggestion. \$\endgroup\$ Commented Jan 9, 2015 at 2:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.