Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self numbers in c++

Hey, my friends and I are trying to beat each other's runtimes for generating "Self Numbers" between 1 and a million. I've written mine in c++ and I'm still trying to shave off precious time.

Here's what I have so far,

#include <iostream>

using namespace std;

bool v[1000000];
int main(void) {
  long non_self = 0;
  for(long i = 1; i < 1000000; ++i) {
    if(!(v[i])) std::cout << i << '\n';
    non_self = i + (i%10) + (i/10)%10 + (i/100)%10 + (i/1000)%10 + (i/10000)%10 +(i/100000)%10;
    v[non_self] = 1;
  }
  std::cout << "1000000" << '\n';
  return 0;
}

The code works fine now, I just want to optimize it. Any tips? Thanks.

like image 879
Anon Avatar asked Jan 12 '10 21:01

Anon


People also ask

What is self number?

In number theory, a self number or Devlali number in a given number base is a natural number that cannot be written as the sum of any other natural number and the individual digits of . 20 is a self number (in base 10), because no such combination can be found (all give a result less than 20; all other.

How do you find the self descriptive number?

A self-descriptive number is an integer n in given base b is b digits long in which each digit at position p (the most significant digit being at position 0 and the least significant at position b – 1) counts how many times a digit p is in n.

Do self generating numbers exist?

Problem B -- Self Numbers Some numbers have more than one generator: for example, 101 has two generators, 91 and 100. A number with no generators is a self-number. There are thirteen self-numbers less than 100: 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, and 97.

What is the 10 digit autobiographical number?

Martin Gardner gives a puzzle about a ten-digit autobiographical number 6210001000 in his book “Mathematical Circus” [1].


1 Answers

I built an alternate C solution that doesn't require any modulo or division operations:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
   int v[1100000];
   int j1, j2, j3, j4, j5, j6, s, n=0;
   memset(v, 0, sizeof(v));
   for (j6=0; j6<10; j6++) {
      for (j5=0; j5<10; j5++) {
         for (j4=0; j4<10; j4++) {
            for (j3=0; j3<10; j3++) {
               for (j2=0; j2<10; j2++) {
                  for (j1=0; j1<10; j1++) {
                     s = j6 + j5 + j4 + j3 + j2 + j1;
                     v[n + s] = 1;
                     n++;
                  }
               }
            }
         }
      }
   }
   for (n=1; n<=1000000; n++) {
      if (!v[n]) printf("%6d\n", n);
   }
}

It generates 97786 self numbers including 1 and 1000000.
With output, it takes

real        0m1.419s
user        0m0.060s
sys         0m0.152s

When I redirect output to /dev/null, it takes

real     0m0.030s
user     0m0.024s
sys      0m0.004s

on my 3 Ghz quad core rig.

For comparison, your version produces the same number of numbers, so I assume we're either both correct or equally wrong; but your version chews up

real    0m0.064s
user    0m0.060s
sys     0m0.000s

under the same conditions, or about 2x as much.

That, or the fact that you're using longs, which is unnecessary on my machine. Here, int goes up to 2 billion. Maybe you should check INT_MAX on yours?

Update

I had a hunch that it may be better to calculate the sum piecewise. Here's my new code:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
   char v[1100000];
   int j1, j2, j3, j4, j5, j6, s, n=0;
   int s1, s2, s3, s4, s5;
   memset(v, 0, sizeof(v));
   for (j6=0; j6<10; j6++) {
      for (j5=0; j5<10; j5++) {
         s5 = j6 + j5;
         for (j4=0; j4<10; j4++) {
            s4 = s5 + j4;
            for (j3=0; j3<10; j3++) {
               s3 = s4 + j3;
               for (j2=0; j2<10; j2++) {
                  s2 = s3 + j2;
                  for (j1=0; j1<10; j1++) {
                     v[s2 + j1 + n++] = 1;
                  }
               }
            }
         }
      }
   }
   for (n=1; n<=1000000; n++) {
      if (!v[n]) printf("%d\n", n);
   }
}

...and what do you know, that brought down the time for the top loop from 12 ms to 4 ms. Or maybe 8, my clock seems to be getting a bit jittery way down there.

State of affairs, Summary

The actual finding of self numbers up to 1M is now taking roughly 4 ms, and I'm having trouble measuring any further improvements. On the other hand, as long as output is to the console, it will continue to take about 1.4 seconds, my best efforts to leverage buffering notwithstanding. The I/O time so drastically dwarfs computation time that any further optimization would be essentially futile. Thus, although inspired by further comments, I've decided to leave well enough alone.

All times cited are on my (pretty fast) machine and are for comparison purposes with each other only. Your mileage may vary.

like image 185
Carl Smotricz Avatar answered Nov 15 '22 19:11

Carl Smotricz