Euler Problem 1 Python Solution

I wanted to start playing with python a bit more so I thought I’d take a look at the project Euler problems. The first problem is to find the sum of all numbers between 1 and 999 that are divisible by 3 or 5.

After solving it and looking on the forums I was kind of shocked that most solutions used loops, iterating over every number between 1 and 1000. A more efficient solution is to use the sum of an arithmetic progression. Which I would have thought is high school Maths (I still needed Wikipedia to prompt me, ho hum). Anyway Here’s my solution:

max = 999

maxthree = ((max/3)*3) + 0.0
maxfive = ((max/5)*5) + 0.0
maxfifteen = ((max/15)*15) + 0.0

multhree = ((maxthree/3)/2)*(3+maxthree)
mulfive = ((maxfive/5)/2)*(5+maxfive)
mulfifteen = ((maxfifteen/15)/2)*(15+maxfifteen)

print multhree + mulfive - mulfifteen

I’d guess about 95% of the solutions used loops.
This is my favourite solution from the forum:

Here is a solution that is reasonably efficient but it works for any list of possible factors, without being overcomplicated (in Java). It took me about 50 minutes to write it.

import java.io.*;
import java.util.*;
import java.lang.Math;



public class Euler1 {


   public static void main(String[] args) {

      // Eueler project problem 1
      // find the sum of all numbers less than N (1000) that are multiples of a given list of factors (3 and 5)
      // the program seeks a balance between speed, memory use, generality and program simplicity

      int[] factors = {3,5};
      int N = 1000;

      int sum = computeSum (factors, N);

      System.err.println(sum);

   }


   public static int computeSum (int[] factors, int N) {

      // efficient calculation using the fact that the pattern of multiples repeats
      // use the produce of the factors as the period length, 
      // the least common multiple would be more efficient but complicates the program

      int M = 1;
      for(int j=0; j<factors.length; j++) {
         int f = factors[j];
         M *= f;
      }

      int k = (N-1)/M;    // number of repeated periods
      int r = (N-1)%M+1;  // remaining length;

      // use average of sum over first and last period time k, plus sum over the remaining numbers 

      int sum = (bruteSum(factors, 1, M+1)+bruteSum(factors,(k-1)*M+1,k*M+1))*k/2 + bruteSum(factors, k*M+1, N);

      return sum;
   }

   public static int bruteSum(int[] factors, int N0, int N1) {

      // computes the answer using a straightforward but inefficient brute force method 

      int sum = 0;
      
      for(int i=N0; i<N1; i++) {
         boolean isAMultiple = false;
         for(int j=0; j<factors.length; j++) {
            int f = factors[j];
            if(i%f == 0) isAMultiple = true;
         }

         if(isAMultiple) {
            sum += i;
         }

      }

      return sum;
         
   }
}

Convert DNA string to binary

Getting fed up with writing this, so here’s some basic code to do it:

#include <iostream>

using namespace std;

uint32_t dna_number(string s) {

  uint32_t num = 0;

  for(size_t n=0;n<s.size();n++) {

    num = num << 2;
    if(s[n] == 'A') num += 0;
    if(s[n] == 'C') num += 1;
    if(s[n] == 'G') num += 2;
    if(s[n] == 'T') num += 3;

  }
  return num;
}

int main(int argc,char **argv) {
  cout << dna_number("AAAAA") << endl;
  cout << dna_number("AAAAC") << endl;
  cout << dna_number("AAAAG") << endl;
  cout << dna_number("AAAAT") << endl;
  cout << dna_number("AAACA") << endl;
  cout << dna_number("AAACC") << endl;
  cout << dna_number("AAACG") << endl;
  cout << dna_number("AAACT") << endl;
  cout << dna_number("AAAGA") << endl;
  cout << dna_number("AAAGC") << endl;
  cout << dna_number("AAAGG") << endl;
  cout << dna_number("AAAGT") << endl;
  cout << dna_number("AAATA") << endl;
  cout << dna_number("AAATC") << endl;
  cout << dna_number("AAATG") << endl;
  cout << dna_number("AAATT") << endl;
  cout << dna_number("AACAA") << endl;
}

string to int conversion

I was recently asked to write a string to int conversion function (without using library functions). I initially came up with a solution using the pow function (which is quite expensive). I had a think about it and found there were a surprising number of solutions. Briefly I came up with the following methods:

Method Summary
Pow my initial pow based solution (after converting a position to an in calculating 10^val)
Mul Rather than using pow generating powers of 10 in the loop (multiplier = multipler*10)
Table Just use a lookup table for the multipliers
Case More of less the same as table, but encode the table in a switch statement (very ugly!)

Results are probably compiler/CPU/platform dependent. But on my Atom Z530 (1.6GHz) based netbook using GCC 4.3.3 I obtained the following results when performing the conversion 10 million times:

Method User time
Pow 43.67s
Mul 28.21s
Table 28.22s
Case 29.13s

There’s a big difference between the pow method and the others, but I was reasonably surprised that multiplier and table based methods performed similarly. It would be interesting to look at the assembler generated for these.

For reference, source code follows (note I sum and output the converted values to prevent the call to string_to_int from being optimised away). I was slightly concerned that something funky /might/ be going on in string::size() however benchmarked with this in and outside the loop and didn’t observe any difference. Note: Following programs don’t process signs, but in terms of benchmarking I don’t believe this should be relevant.

Pow:

#include <string>
#include <iostream>
#include <math.h>
#include <stdlib.h>

using namespace std;

int string_to_int(string s) {

  int output=0;

  for(int n=0;n<s.size();n++) {
    int cval = s[n]-'0';
    output += cval*pow(10,s.size()-n-1);
  }

  return output;
}

int main() {

  // Simple tests
  cout << "1 is: " << string_to_int("1") << endl;
  cout << "10 is: " << string_to_int("10") << endl;
  cout << "14532 is: " << string_to_int("14532") << endl;

  int rsum=0;
  for(int i=0;i<10000000;i++) {
    string s;
    int numlen = rand()%11;
    for(int n=0;n<numlen;n++) {
      int rval;
      if((numlen==10) && (n==0)) { rval = rand()%2;  }
                           else  { rval = rand()%10; }
      s.push_back('0'+rval);
    }
    int v = string_to_int(s);
    rsum += v;
  }
  cout << rsum << endl;
}

Mul:

#include <string>
#include <iostream>
#include <math.h>
#include <stdlib.h>

using namespace std;

int string_to_int(string s) {

  int output=0;

  int mul=1;
  for(int n=s.size()-1;n>=0;n--) {
    int cval = s[n]-'0';
    output += cval*mul;
    mul = mul * 10;
  }

  return output;
}

int main() {

  // Simple tests
  cout << "1 is: " << string_to_int("1") << endl;
  cout << "10 is: " << string_to_int("10") << endl;
  cout << "14532 is: " << string_to_int("14532") << endl;

  int rsum=0;
  for(int i=0;i<10000000;i++) {
    string s;
    int numlen = rand()%11;
    for(int n=0;n<numlen;n++) {
      int rval;
      if((numlen==10) && (n==0)) { rval = rand()%2;  }
                           else  { rval = rand()%10; }
      s.push_back('0'+rval);
    }
    int v = string_to_int(s);
    rsum += v;
  }
  cout << rsum << endl;
}

Table:

#include <string>
#include <iostream>
#include <math.h>
#include <stdlib.h>

using namespace std;

const int powtable [] = { 1,
                          10,
                          100,
                          1000,
                          10000,
                          100000,
                          1000000,
                          10000000,
                          100000000,
                          1000000000
                        };

int string_to_int(string s) {

  int output=0;

  for(int n=s.size()-1;n>=0;n--) {
    int cval = s[n]-'0';
    output += cval*(powtable[s.size()-n-1]);
  }

  return output;
}

int main() {

  // Simple tests
  cout << "1 is: " << string_to_int("1") << endl;
  cout << "10 is: " << string_to_int("10") << endl;
  cout << "14532 is: " << string_to_int("14532") << endl;

  int rsum=0;
  for(int i=0;i<10000000;i++) {
    string s;
    int numlen = rand()%11;
    for(int n=0;n<numlen;n++) {
      int rval;
      if((numlen==10) && (n==0)) { rval = rand()%2;  }
                           else  { rval = rand()%10; }
      s.push_back('0'+rval);
    }
    int v = string_to_int(s);
    rsum += v;
  }
  cout << rsum << endl;
}

Case:

#include <string>
#include <iostream>
#include <math.h>
#include <stdlib.h>

using namespace std;

int string_to_int(string s) {

  int output=0;

  int pos=0;
  for(int n=s.size()-1;n>=0;n--) {
    int cval = s[n]-'0';

    switch(pos) {
      case 0:
        output += cval*1;
        break;
      case 1:
        output += cval*10;
        break;
      case 2:
        output += cval*100;
        break;
      case 3:
        output += cval*1000;
        break;
      case 4:
        output += cval*10000;
        break;
      case 5:
        output += cval*100000;
        break;
      case 6:
        output += cval*1000000;
        break;
      case 7:
        output += cval*10000000;
        break;
      case 9:
        output += cval*100000000;
        break;
      case 10:
        output += cval*1000000000;
        break;
    }

    pos++;
  }

  return output;
}

int main() {

  // Simple tests
  cout << "1 is: " << string_to_int("1") << endl;
  cout << "10 is: " << string_to_int("10") << endl;
  cout << "14532 is: " << string_to_int("14532") << endl;

  int rsum=0;
  for(int i=0;i<10000000;i++) {
    string s;
    int numlen = rand()%11;
    for(int n=0;n<numlen;n++) {
      int rval;
      if((numlen==10) && (n==0)) { rval = rand()%2;  }
                           else  { rval = rand()%10; }
      s.push_back('0'+rval);
    }
    int v = string_to_int(s);
    rsum += v;
  }
  cout << rsum << endl;
}

Six People at a Party

When there are six people at a party there will always be either:

1. At least three people who all know each other.
2. At least three people, none of whom know each other.

One of the above statements must be true. You can’t have a party of six people where everybody knows one other person but there’s no group of three people none of whom know each other, for example.

This can be shown using graph theory.

1. We represent the group of six people with a graph.
2. Edges between vertices are of two types (know or don’t know).
3. Pick a vertex at random. Any vertex will have at least 3 edges of one type. For example:

Where known/not known is represented by solid/dotted.

4. Now, either one of the edges bc, bd or cd is solid or not.

5. If one edge is solid, 3 people all know/don’t know each other (abc,acd, or abd).

6. If none of the edges exists then bcd know/don’t know each other.

Notes

The dot file used to generate the graph above:

graph graphname {
  a [shape=circle];
  b [shape=circle];
  c [shape=circle];
  d [shape=circle]; 
  e [shape=circle];
  f [shape=circle];


  a -- b;
  a -- c;
  a -- d;
  a -- e [style=dotted];
  a -- f [style=dotted];
}

I encountered this problem in “Introduction to Graph Theory” by Robin J. Wilson (great book).

You can read more about the problem and it’s generalisation on wikipedia here.