1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
// nines.cpp
//----------------------------------------------------------------------------
// Calculate the number of digits produced when writing 1 to N
//----------------------------------------------------------------------------
// See http://www.cplusplus.com/forum/general/59883
// Copyright (c) 2012 Michael Thomas Greer
// Distributed under the Boost Software License, Version 1.0.
// ( See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt )
//
#include <algorithm>
#include <cmath>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
using namespace std;
//----------------------------------------------------------------------------
// number
//----------------------------------------------------------------------------
// description
// A VERY SIMPLE bignum class.
// Please don't base your bignums on this!
// It does things wonkily, and only does enough to make += -= and * work.
// Some crunching used to discourage use and to preserve space.
//
// Numbers are unsigned, and represented as their textual equivalent.
//
#define bm bind2nd( minus <char> (), '0' )
#define bp( x ) bind2nd( plus <char> (), x )
#define c const
#define e erase
#define f for_each
#define h *this
#define i insert
#define l length()
#define N number
#define o operator
#define p public
#define r return
#define rb rbegin()
#define re rend()
#define ri reverse_iterator x =
#define S string
#define t transform
#define ul unsigned long
#define T 10
#define Z '0'
struct number: string
{ p: N(ul _=0){o=(_);}
N& o=(ul _){clear();do i(0,1,(_%T)+Z);while(_/=T);r h;}
N(c S&_):S(_){}
struct y{int k; y(int k):k(k){}void o()(char&_){k+=_-Z-Z;_=(k%T)+Z;k/=T;}};
N& o+=(c N&_){i(0,((l<_.l)?(_.l-l):0)+1,Z);
ri t(_.rb,_.re,rb,rb,plus<char>());
t(x,re,x,bp(Z));f(rb,re,bp(9));f(rb,re,y(0));if(o[](0)==Z) e(0,1);r h;}
N& o-=(c N&_){t(rb,re,rb,bp(9));ri t(rb,rb+_.l,_.rb,rb,minus<char>());
t(x,re,x,bm);t(rb,re,rb,bp(Z+Z));f(rb,re,y(1));e(0,find_first_not_of(Z));
r h;}
N o*(c N&_)c{if(l<_.l)r _.o*(h);N R;R.resize(l+_.l,0);int y=0;
for(size_t ni=0;ni<_.l;ni++){int rx=ni;for(size_t x=0;x<l;x++){
y+=R[R.l-1-rx]+((o[](l-1-x)-Z)*(_[_.l-1-ni]-Z));R[R.l-1-rx++]=y%T;y/=T;}
R[R.l-1-rx]=y;y=0;}t(R.rb,R.re,R.rb,bp(Z));R.e(0,R.find_first_not_of(Z));
if(R.empty())R=S("0");r R;}
};
//----------------------------------------------------------------------------
// Main Program
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Method 1: Cheat by printing all the numbers and counting how many
// characters were printed.
//
unsigned long cheat( unsigned long n )
{
ostringstream ss;
for (unsigned long x = 1; x <= n; x++)
ss << x;
return ss.str().length();
}
//----------------------------------------------------------------------------
// Method 2: Using machine integers
//
// This method requires a little geometric thinking. If you want to count
// how many desks there are in a room, you multiply the number of desks
// across the front and the number of desks deep.
//
// D D D D
// D D D D 3 desks deep 4 x 3 desks is 12 desks total
// D D D D
// 4 desks across the front
//
// This algorithm works on the same principle. Take the argument number (n)
// and determine the number of digits wide it is (w).
//
// Multiply n and w together.
//
// Of course, smaller numbers may not be as wide as larger numbers. We can
// think of this as missing desks. (Where desks == digits.) Fortunately, the
// counting numbers have missing desk-digits which follow a friendly pattern
// involving NINES.
//
// If w is 1, then we don't need to subtract anything, since there aren't any
// missing desk-digits.
//
// If w is 2, then we need to subtract 9 for the desks in rows 1 through 9.
//
// If w is 3, then we ALSO need to subtract 99 for the desks in rows 1 through
// 99.
//
// And so on. The fun trick is that you can easily write the sum of the
// sequence 9 + 99 + 999 + 9999 + ... for any term by a simple trick and a
// single subtraction:
//
// If we wish to know the sum of terms 1 through m, write m ones followed by
// a single zero, and subtract m from that number.
//
// For example, if m is 3, then write "1110 - 3"; the answer is 1107. The sum
// 9 + 99 + 999 is exactly 1107. Neat, no?
//
// Using mathematics, the way to get all those ones is a simple trick with
// exponents. To get 'm' ones:
//
// ((10**m) - 1) / 9 (where ** represents exponentiation)
//
// We will also use w-1 for m. Take a second to think about why.
// So the equation boils down to:
//
// (n * w) - ( (((10**(w-1)) - 1) / 9 * 10) - (w-1) )
//
// :O)
//
unsigned long digits_in_1_to_N( unsigned long n )
{
if (n == 0) return 0;
unsigned long w = floor( log10( n ) ) + 1;
n *= w;
w -= 1;
n -= ((unsigned long)((pow( 10, w ) - 1) / 9) * 10) - w;
return n;
}
//----------------------------------------------------------------------------
// Method 3: Using bignums
// This method is otherwise exactly the same as Method 3 above.
//
number& big_digits_in_1_to_N( number& n )
{
if (n == "0") return n;
unsigned long w = n.length();
n = n * number( w );
w -= 1;
n += number( w );
n -= number( string( w, '1' ) + "0" ); // Forget math, we're using strings. ;O)
return n;
}
//----------------------------------------------------------------------------
int main()
{
string s;
unsigned long n;
cout << "Calculate the number of digits needed to count from 1 to n.\n";
cout << "n? ";
getline( cin, s );
if (s.find_first_not_of( "0123456789" ) != string::npos)
{
cout << "n must be a whole number, foo.\n";
return 1;
}
istringstream ss( s );
ss >> n;
if (ss)
{
if (n < 10000000) // cheat method is SLOW, so we'll apply an arbitrary cut-off
{
cout << "ostream.str().length() --> " << cheat( n ) << endl;
}
if (n > 489564266)
cout << "(notice how n is now too big for the machine method to work properly?)\n";
cout << "unsigned int calculated --> " << digits_in_1_to_N( n ) << endl;
}
number nn( s );
cout << "bignum calculated --> " << big_digits_in_1_to_N( nn ) << endl;
return 0;
}
|