Related
I want to read digit by digit the decimals of the sqrt of 5 in C.
The square root of 5 is 2,23606797749979..., so this'd be the expected output:
2
3
6
0
6
7
9
7
7
...
I've found the following code:
#include<stdio.h>
void main()
{
int number;
float temp, sqrt;
printf("Provide the number: \n");
scanf("%d", &number);
// store the half of the given number e.g from 256 => 128
sqrt = number / 2;
temp = 0;
// Iterate until sqrt is different of temp, that is updated on the loop
while(sqrt != temp){
// initially 0, is updated with the initial value of 128
// (on second iteration = 65)
// and so on
temp = sqrt;
// Then, replace values (256 / 128 + 128 ) / 2 = 65
// (on second iteration 34.46923076923077)
// and so on
sqrt = ( number/temp + temp) / 2;
}
printf("The square root of '%d' is '%f'", number, sqrt);
}
But this approach stores the result in a float variable, and I don't want to depend on the limits of the float types, as I would like to extract like 10,000 digits, for instance. I also tried to use the native sqrt() function and casting it to string number using this method, but I faced the same issue.
What you've asked about is a very hard problem, and whether it's even possible to do "one by one" (i.e. without working space requirement that scales with how far out you want to go) depends on both the particular irrational number and the base you want it represented in. For example, in 1995 when a formula for pi was discovered that allows computing the nth binary digit in O(1) space, this was a really big deal. It was not something people expected to be possible.
If you're willing to accept O(n) space, then some cases like the one you mentioned are fairly easy. For example, if you have the first n digits of the square root of a number as a decimal string, you can simply try appending each digit 0 to 9, then squaring the string with long multiplication (same as you learned in grade school), and choosing the last one that doesn't overshoot. Of course this is very slow, but it's simple. The easy way to make it a lot faster (but still asymptotically just as bad) is using an arbitrary-precision math library in place of strings. Doing significantly better requires more advanced approaches and in general may not be possible.
As already noted, you need to change the algorithm into a digit-by-digit one (there are some examples in the Wikipedia page about the methods of computing of the square roots) and use an arbitrary precision arithmetic library to perform the calculations (for instance, GMP).
In the following snippet I implemented the before mentioned algorithm, using GMP (but not the square root function that the library provides). Instead of calculating one decimal digit at a time, this implementation uses a larger base, the greatest multiple of 10 that fits inside an unsigned long, so that it can produce 9 or 18 decimal digits at every iteration.
It also uses an adapted Newton method to find the actual "digit".
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <gmp.h>
unsigned long max_ul(unsigned long a, unsigned long b)
{
return a < b ? b : a;
}
int main(int argc, char *argv[])
{
// The GMP functions accept 'unsigned long int' values as parameters.
// The algorithm implemented here can work with bases other than 10,
// so that it can evaluate more than one decimal digit at a time.
const unsigned long base = sizeof(unsigned long) > 4
? 1000000000000000000
: 1000000000;
const unsigned long decimals_per_digit = sizeof(unsigned long) > 4 ? 18 : 9;
// Extract the number to be square rooted and the desired number of decimal
// digits from the command line arguments. Fallback to 0 in case of errors.
const unsigned long number = argc > 1 ? atoi(argv[1]) : 0;
const unsigned long n_digits = argc > 2 ? atoi(argv[2]) : 0;
// All the variables used by GMP need to be properly initialized before use.
// 'c' is basically the remainder, initially set to the original number
mpz_t c;
mpz_init_set_ui(c, number);
// At every iteration, the algorithm "move to the left" by two "digits"
// the reminder, so it multplies it by base^2.
mpz_t base_squared;
mpz_init_set_ui(base_squared, base);
mpz_mul(base_squared, base_squared, base_squared);
// 'p' stores the digits of the root found so far. The others are helper variables
mpz_t p;
mpz_init_set_ui(p, 0UL);
mpz_t y;
mpz_init(y);
mpz_t yy;
mpz_init(yy);
mpz_t dy;
mpz_init(dy);
mpz_t dx;
mpz_init(dx);
mpz_t pp;
mpz_init(pp);
// Timing, for testing porpuses
clock_t start = clock(), diff;
unsigned long x_max = number;
// Each "digit" correspond to some decimal digits
for (unsigned long i = 0,
last = (n_digits + decimals_per_digit) / decimals_per_digit + 1UL;
i < last; ++i)
{
// Find the greatest x such that: x * (2 * base * p + x) <= c
// where x is in [0, base), using a specialized Newton method
// pp = 2 * base * p
mpz_mul_ui(pp, p, 2UL * base);
unsigned long x = x_max;
for (;;)
{
// y = x * (pp + x)
mpz_add_ui(yy, pp, x);
mpz_mul_ui(y, yy, x);
// dy = y - c
mpz_sub(dy, y, c);
// If y <= c we have found the correct x
if ( mpz_sgn(dy) <= 0 )
break;
// Newton's step: dx = dy/y' where y' = 2 * x + pp
mpz_add_ui(yy, yy, x);
mpz_tdiv_q(dx, dy, yy);
// Update x even if dx == 0 (last iteration)
x -= max_ul(mpz_get_si(dx), 1);
}
x_max = base - 1;
// The actual format of the printed "digits" is up to you
if (i % 4 == 0)
{
if (i == 0)
printf("%lu.", x);
putchar('\n');
}
else
printf("%018lu", x);
// p = base * p + x
mpz_mul_ui(p, p, base);
mpz_add_ui(p, p, x);
// c = (c - y) * base^2
mpz_sub(c, c, y);
mpz_mul(c, c, base_squared);
}
diff = clock() - start;
long int msec = diff * 1000L / CLOCKS_PER_SEC;
printf("\n\nTime taken: %ld.%03ld s\n", msec / 1000, msec % 1000);
// Final cleanup
mpz_clear(c);
mpz_clear(base_squared);
mpz_clear(p);
mpz_clear(pp);
mpz_clear(dx);
mpz_clear(y);
mpz_clear(dy);
mpz_clear(yy);
}
You can see the outputted digits here.
Your title says:
How to compute the digits of an irrational number one by one?
Irrational numbers are not limited to most square roots. They also include numbers of the form log(x), exp(z), sin(y), etc. (transcendental numbers). However, there are some important factors that determine whether or how fast you can compute a given irrational number's digits one by one (that is, from left to right).
Not all irrational numbers are computable; that is, no one has found a way to approximate them to any desired length (whether by a closed form expression, a series, or otherwise).
There are many ways numbers can be expressed, such as by their binary or decimal expansions, as continued fractions, as series, etc. And there are different algorithms to compute a given number's digits depending on the representation.
Some formulas compute a given number's digits in a particular base (such as base 2), not in an arbitrary base.
For example, besides the first formula to extract the digits of π without computing the previous digits, there are other formulas of this type (known as BBP-type formulas) that extract the digits of certain irrational numbers. However, these formulas only work for a particular base, not all BBP-type formulas have a formal proof, and most importantly, not all irrational numbers have a BBP-type formula (essentially, only certain log and arctan constants do, not numbers of the form exp(x) or sqrt(x)).
On the other hand, if you can express an irrational number as a continued fraction (which all real numbers have), you can extract its digits from left to right, and in any base desired, using a specific algorithm. What is more, this algorithm works for any real number constant, including square roots, exponentials (e and exp(x)), logarithms, etc., as long as you know how to express it as a continued fraction. For an implementation see "Digits of pi and Python generators". See also Code to Generate e one Digit at a Time.
I am trying to find the largest prime factor of a huge number in C ,for small numbers like 100 or even 10000 it works fine but fails (By fail i mean it keeps running and running for tens of minutes on my core2duo and i5) for very big target numbers (See code for the target number.)
Is my algorithm correct?
I am new to C and really struggling with big numbers. What i want is correction or guidance not a solution i can do this using python with bignum bindings and stuff (I have not tried yet but am pretty sure) but not in C. Or i might have done some tiny mistake that i am too tired to realize , anyways here is the code i wrote:
#include <stdio.h>
// To find largest prime factor of target
int is_prime(unsigned long long int num);
long int main(void) {
unsigned long long int target = 600851475143;
unsigned long long int current_factor = 1;
register unsigned long long int i = 2;
while (i < target) {
if ( (target % i) == 0 && is_prime(i) && (i > current_factor) ) { //verify i as a prime factor and greater than last factor
current_factor = i;
}
i++;
}
printf("The greates is: %llu \n",current_factor);
return(0);
}
int is_prime (unsigned long long int num) { //if num is prime 1 else 0
unsigned long long int z = 2;
while (num > z && z !=num) {
if ((num % z) == 0) {return 0;}
z++;
}
return 1;
}
600 billion iterations of anything will take some non-trivial amount of time. You need to substantially reduce this.
Here's a hint: Given an arbitrary integer value x, if we discover that y is a factor, then we've implicitly discovered that x / y is also a factor. In other words, factors always come in pairs. So there's a limit to how far we need to iterate before we're doing redundant work.
What is that limit? Well, what's the crossover point where y will be greater than x / y?
Once you've applied this optimisation to the outer loop, you'll find that your code's runtime will be limited by the is_prime function. But of course, you may apply a similar technique to that too.
By iterating until the square root of the number, we can get all of it's factors.( factor and N/factor and factor<=sqrt(N)). Under this small idea the solution exists. Any factor less than the sqrt(N) we check, will have corresponding factor larger than sqrt(N). So we only need to check up to the sqrt(N), and then we can get the remaining factors.
Here you don't need to use explicitly any prime finding algorithm. The factorization logic itself will deduce whether the target is prime or not. So all that is left is to check the pairwise factors.
unsigned long long ans ;
for(unsigned long long i = 2; i<=target/i; i++)
while(target % i == 0){
ans = i;
target/=i;
}
if( target > 1 ) ans = target; // that means target is a prime.
//print ans
Edit: A point to be added (chux)- i*i in the earlier code is may lead to overflow which can be avoided if we use i<=target/i.
Also another choice would be to have
unsigned long long sqaure_root = isqrt(target);
for(unsigned long long i = 2; i<=square_root; i++){
...
}
Here note than use of sqrt is not a wise choice since -
mixing of double math with an integer operation is prone to round-off errors.
For target given the answer will be 6857.
Code has 2 major problems
The while (i < target) loop is very inefficient. Upon finding a factor, target could be reduced to target = target / i;. Further, a factor i could occur multiple times. Fix not shown.
is_prime(n) is very inefficient. Its while (num > z && z !=num) could loop n time. Here too, use the quotient to limit the iterations to sqrt(n) times.
int is_prime (unsigned long long int num) {
unsigned long long int z = 2;
while (z <= num/z) {
if ((num % z) == 0) return 0;
z++;
}
return num > 1;
}
Nothing is wrong, it just needs optimization, for example:
int is_prime(unsigned long long int num) {
if (num == 2) {
return (1); /* Special case */
}
if (num % 2 == 0 || num <= 1) {
return (0);
}
unsigned long long int z = 3; /* We skipped the all even numbers */
while (z < num) { /* Do a single test instead of your redundant ones */
if ((num % z) == 0) {
return 0;
}
z += 2; /* Here we go twice as fast */
}
return 1;
}
Also the big other problem is while (z < num) but since you don't want the solution i let you find how to optimize that, similarly look out by yourself the first function.
EDIT: Someone else posted 50 seconds before me the array-list of primes solution which is the best but i chose to give an easy solution since you are just a beginner and manipulating arrays may not be easy at first (need to learn pointers and stuff).
is_prime has a chicken-and-egg problem in that you need to test num only against other primes. So you don't need to check against 9 because that is a multiple of 3.
is_prime could maintain an array of primes and each time a new num is tested that is a pime, it can be added to the array. num isr tested against each prime in the array and if it is not divisable by any of the primes in the array, it is itself a prime and is added to the array. The aray needs to be malloc'd and relloc'd unless there is a formue to calculate the number of primes up intil your target (I believe such formula does not exist).
EDIT: the number of primes to test for the target 600,851,475,143 will be approximately 7,500,000,000 and the table could run out of memory.
The approach can be adapted as follows:
to use unsiged int up until primes of UINT_max
to use unsigned long long int for primes above that
to use brute force above a certain memory consumption.
UINT_MAX is defined as 4,294,967,295 and would cover the primes up to around 100,000,000,000 and would cost 7.5*4= 30Gb
See also The Prime Pages.
For an assignment we are required to write a division algorithm in order to complete a certain question using just addition and recursion. I found that, without using tail recursion, the naive repeated subtraction implementation can easily result in a stack overflow. So doing a quick analysis of this method, and correct me if I'm wrong, shows that if you divide A by B, with n and m binary digits respectively, it should be exponential in n-m. I actually get
O( (n-m)*2^(n-m) )
since you need to subtract an m binary digit number from an n binary digit number 2^(n-m) times in order to drop the n digit number to an n-1 digit number, and you need to do this n-m times to get a number with at most m digits in the repeated subtraction division, so the runtime should be as mentioned. Again, I very well may be wrong so someone please correct me if I am. This is assuming O(1) addition since I'm working with fixed size integers. I suppose with fixed size integers one could argue the algorithm is O(1).
Back to my main question. I developed a different method to perform integer division which works much better, even when using it recursively, based on the idea that for
P = 2^(k_i) + ... 2^(K_0)
we have
A/B = (A - B*P)/B + P
The algorithm goes as follows to caclulate A/B:
input:
A, B
i) Set Q = 0
ii) Find the largest K such that B * 2^K <= A < B * 2(K + 1)
iii) Q -> Q + 2^K
iv) A -> A - B * 2^k
v) Repeat steps ii) through iv) until A <= B
vi) Return Q (and A if you want the remainder)
with the restrictions of using only addition, I simply add B to itself on each recursive call, however here is my code without recursion and with the use of shifts instead of addition.
int div( unsigned int m, unsigned int n )
{
// q is a temporary n, sum is the quotient
unsigned int q, sum = 0;
int i;
while( m > n )
{
i = 0;
q = n;
// double q until it's larger than m and record the exponent
while( q <= m )
{
q <<= 1;
++i;
}
i--;
q >>= 1; // q is one factor of 2 too large
sum += (1<<i); // add one bit of the quotient
m -= q; // new numerator
}
return sum;
}
I feel that sum |= (1<<i) may be more appropriate in order to emphasize I'm dealing with a binary representation, but it didn't seem to give any performance boost and may make it harder to understand. So, if M and N are the number of bits in m and n respectively, an analysis suggests the inner loop is performed M - N times and each time the outer loop is completed that m looses one bit, and it must also be completed M - N times in order for the condition m <= n so I get that it's O( (M - N)^2 ).
So after all of that, I am asking if I am correct about the runtime of the algorithm and whether it can be improved upon?
Your algorithm is pretty good and your analysis of the running time is correct, but you don't need to do the inner loop every time:
unsigned div(unsigned num, unsigned den)
{
//TODO check for divide by zero
unsigned place=1;
unsigned ret=0;
while((num>>1) >= den) //overflow-safe check
{
place<<=1;
den<<=1;
}
for( ;place>0; place>>=1,den>>=1)
{
if (num>=den)
{
num-=den;
ret+=place;
}
}
return ret;
}
That makes it O(M-N)
Forgive me if I am being a bit silly, but I have only very recently started programming, and am maybe a little out of my depth doing Problem 160 on Project Euler. I have made some attempts at solving it but it seems that going through 1tn numbers will take too long on any personal computer, so I guess I should be looking into the mathematics to find some short-cuts.
Project Euler Problem 160:
For any N, let f(N) be the last five digits before the trailing zeroes
in N!. For example,
9! = 362880 so f(9)=36288 10! = 3628800 so f(10)=36288 20! =
2432902008176640000 so f(20)=17664
Find f(1,000,000,000,000)
New attempt:
#include <stdio.h>
main()
{
//I have used long long ints everywhere to avoid possible multiplication errors
long long f; //f is f(1,000,000,000,000)
f = 1;
for (long long i = 1; i <= 1000000000000; i = ++i){
long long p;
for (p = i; (p % 10) == 0; p = p / 10) //p is i without proceeding zeros
;
p = (p % 1000000); //p is last six nontrivial digits of i
for (f = f * p; (f % 10) == 0; f = f / 10)
;
f = (f % 1000000);
}
f = (f % 100000);
printf("f(1,000,000,000,000) = %d\n", f);
}
Old attempt:
#include <stdio.h>
main()
{
//This part of the programme removes the zeros in factorials by dividing by 10 for each factor of 5, and finds f(1,000,000,000,000) inductively
long long int f, m; //f is f(n), m is 10^k for each multiple of 5
short k; //Stores multiplicity of 5 for each multiple of 5
f = 1;
for (long long i = 1; i <= 100000000000; ++i){
if ((i % 5) == 0){
k = 1;
for ((m = i / 5); (m % 5) == 0; m = m / 5) //Computes multiplicity of 5 in factorisation of i
++k;
m = 1;
for (short j = 1; j <= k; ++j) //Computes 10^k
m = 10 * m;
f = (((f * i) / m) % 100000);
}
else f = ((f * i) % 100000);
}
printf("f(1,000,000,000,000) = %d\n", f);
}
The problem is:
For any N, let f(N) be the last five digits before the trailing zeroes in N!. Find f(1,000,000,000,000)
Let's rephrase the question:
For any N, let g(N) be the last five digits before the trailing zeroes in N. For any N, let f(N) be g(N!). Find f(1,000,000,000,000).
Now, before you write the code, prove this assertion mathematically:
For any N > 1, f(N) is equal to g(f(N-1) * g(N))
Note that I have not proved this myself; I might be making a mistake here. (UPDATE: It appears to be wrong! We'll have to give this more thought.) Prove it to your satisfaction. You might want to start by proving some intermediate results, like:
g(x * y) = g(g(x) * g(y))
And so on.
Once you have obtained a proof of this result, now you have a recurrence relation that you can use to find any f(N), and the numbers you have to deal with don't ever get much larger than N.
Prod(n->k)(k*a+c) mod a <=> c^k mod a
For example
prod[ 3, 1000003, 2000003,... , 999999000003 ] mod 1000000
equals
3^(1,000,000,000,000/1,000,000) mod 1000000
And number of trailing 0 in N! equals to number of 5 in factorisation of N!
I would compute the whole thing and then separate first nonzero digits from LSB ...
but for you I think is better this:
1.use bigger base
any number can be rewrite as sum of multiplies of powers of the same number (base)
like 1234560004587786542 can be rewrite to base b=1000 000 000 like this:
1*b^2 + 234560004*b^1 + 587786542*b^0
2.when you multiply then lower digit is dependent only on lowest digits of multiplied numbers
A*B = (a0*b^0+a1*b^1+...)*(b0*b^0+b1*b^1+...)
= (a0*b0*b^0)+ (...*b^1) + (...*b^2)+ ...
3.put it together
for (f=1,i=1;i<=N;i++)
{
j=i%base;
// here remove ending zeroes from j
f*=j;
// here remove ending zeroes from f
f%=base;
}
do not forget that variable f has to be big enough for base^2
and base has to be at least 2 digits bigger then 100000 to cover 5 digits and overflows to zero
base must be power of 10 to preserve decimal digits
[edit1] implementation
uint<2> f,i,j,n,base; // mine 64bit unsigned ints (i use 32bit compiler/app)
base="10000000000"; // base >= 100000^2 ... must be as string to avoid 32bit trunc
n="20"; // f(n) ... must be as string to avoid 32bit trunc
for (f=1,i=1;i<=n;i++)
{
j=i%base;
for (;(j)&&((j%10).iszero());j/=10);
f*=j;
for (;(f)&&((f%10).iszero());f/=10);
f%=base;
}
f%=100000;
int s=f.a[1]; // export low 32bit part of 64bit uint (s is the result)
It is too slow :(
f(1000000)=12544 [17769.414 ms]
f( 20)=17664 [ 0.122 ms]
f( 10)=36288 [ 0.045 ms]
for more speed or use any fast factorial implementation
[edit2] just few more 32bit n! factorials for testing
this statement is not valid :(
//You could attempt to exploit that
//f(n) = ( f(n%base) * (f(base)^floor(n/base)) )%base
//do not forget that this is true only if base fulfill the conditions above
luckily this one seems to be true :) but only if (a is much much bigger then b and a%base=0)
g((a+b)!)=g(g(a!)*g(b!))
// g mod base without last zeroes...
// this can speed up things a lot
f( 1)=00001
f( 10)=36288
f( 100)=16864
f( 1,000)=53472
f( 10,000)=79008
f( 100,000)=56096
f( 1,000,000)=12544
f( 10,000,000)=28125
f( 1,000,100)=42016
f( 1,000,100)=g(??????12544*??????16864)=g(??????42016)->42016
the more is a closer to b the less valid digits there are!!!
that is why f(1001000) will not work ...
I'm not an expert project Euler solver, but some general advice for all Euler problems.
1 - Start by solving the problem in the most obvious way first. This may lead to insights for later attempts
2 - Work the problem for a smaller range. Euler usually give an answer for the smaller range that you can use to check your algorithm
3 - Scale up the problem and work out how the problem will scale, time-wise, as the problem gets bigger
4 - If the solution is going to take longer than a few minutes, it's time to check the algorithm and come up with a better way
5 - Remember that Euler problems always have an answer and rely on a combination of clever programming and clever mathematics
6 - A problem that has been solved by many people cannot be wrong, it's you that's wrong!
I recently solved the phidigital number problem (Euler's site is down, can't look up the number, it's quite recent at time of posting) using exactly these steps. My initial brute-force algorithm was going to take 60 hours, I took a look at the patterns solving to 1,000,000 showed and got the insight to find a solution that took 1.25s.
It might be an idea to deal with numbers ending 2,4,5,6,8,0 separately. Numbers ending 1,3,7,9 can not contribute to a trailing zeros. Let
A(n) = 1 * 3 * 7 * 9 * 11 * 13 * 17 * 19 * ... * (n-1).
B(n) = 2 * 4 * 5 * 6 * 8 * 10 * 12 * 14 * 15 * 16 * 18 * 20 * ... * n.
The factorial of n is A(n)*B(n). We can find the last five digits of A(n) quite easily. First find A(100,000) MOD 100,000 we can make this easier by just doing multiplications mod 100,000. Note that A(200,000) MOD 100,000 is just A(100,000)*A(100,000) MOD 100,000 as 100,001 = 1 MOD 100,000 etc. So A(1,000,000,000,000) is just A(100,000)^10,000,000 MOD 100,000.
More care is needed with 2,4,5,6,8,0 you'll need to track when these add a trailing zero. Obviously whenever we multiply by numbers ending 2 or 5 we will end up with a zero. However there are cases when you can get two zeros 25*4 = 100.
I was looking at another question (here) where someone was looking for a way to get the square root of a 64 bit integer in x86 assembly.
This turns out to be very simple. The solution is to convert to a floating point number, calculate the sqrt and then convert back.
I need to do something very similar in C however when I look into equivalents I'm getting a little stuck. I can only find a sqrt function which takes in doubles. Doubles do not have the precision to store large 64bit integers without introducing significant rounding error.
Is there a common math library that I can use which has a long double sqrt function?
There is no need for long double; the square root can be calculated with double (if it is IEEE-754 64-bit binary). The rounding error in converting a 64-bit integer to double is nearly irrelevant in this problem.
The rounding error is at most one part in 253. This causes an error in the square root of at most one part in 254. The sqrt itself has a rounding error of less than one part in 253, due to rounding the mathematical result to the double format. The sum of these errors is tiny; the largest possible square root of a 64-bit integer (rounded to 53 bits) is 232, so an error of three parts in 254 is less than .00000072.
For a uint64_t x, consider sqrt(x). We know this value is within .00000072 of the exact square root of x, but we do not know its direction. If we adjust it to sqrt(x) - 0x1p-20, then we know we have a value that is less than, but very close to, the square root of x.
Then this code calculates the square root of x, truncated to an integer, provided the operations conform to IEEE 754:
uint64_t y = sqrt(x) - 0x1p-20;
if (2*y < x - y*y)
++y;
(2*y < x - y*y is equivalent to (y+1)*(y+1) <= x except that it avoids wrapping the 64-bit integer if y+1 is 232.)
Function sqrtl(), taking a long double, is part of C99.
Note that your compilation platform does not have to implement long double as 80-bit extended-precision. It is only required to be as wide as double, and Visual Studio implements is as a plain double. GCC and Clang do compile long double to 80-bit extended-precision on Intel processors.
Yes, the standard library has sqrtl() (since C99).
If you only want to calculate sqrt for integers, using divide and conquer should find the result in max 32 iterations:
uint64_t mysqrt (uint64_t a)
{
uint64_t min=0;
//uint64_t max=1<<32;
uint64_t max=((uint64_t) 1) << 32; //chux' bugfix
while(1)
{
if (max <= 1 + min)
return min;
uint64_t sqt = min + (max - min)/2;
uint64_t sq = sqt*sqt;
if (sq == a)
return sqt;
if (sq > a)
max = sqt;
else
min = sqt;
}
Debugging is left as exercise for the reader.
Here we collect several observations in order to arrive to a solution:
In standard C >= 1999, it is garanted that non-netative integers have a representation in bits as one would expected for any base-2 number.
----> Hence, we can trust in bit manipulation of this type of numbers.
If x is a unsigned integer type, tnen x >> 1 == x / 2 and x << 1 == x * 2.
(!) But: It is very probable that bit operations shall be done faster than their arithmetical counterparts.
sqrt(x) is mathematically equivalent to exp(log(x)/2.0).
If we consider truncated logarithms and base-2 exponential for integers, we could obtain a fair estimate: IntExp2( IntLog2(x) / 2) "==" IntSqrtDn(x), where "=" is informal notation meaning almost equatl to (in the sense of a good approximation).
If we write IntExp2( IntLog2(x) / 2 + 1) "==" IntSqrtUp(x), we obtain an "above" approximation for the integer square root.
The approximations obtained in (4.) and (5.) are a little rough (they enclose the true value of sqrt(x) between two consecutive powers of 2), but they could be a very well starting point for any algorithm that searchs for the square roor of x.
The Newton algorithm for square root could be work well for integers, if we have a good first approximation to the real solution.
http://en.wikipedia.org/wiki/Integer_square_root
The final algorithm needs some mathematical comprobations to be plenty sure that always work properly, but I will not do it right now... I will show you the final program, instead:
#include <stdio.h> /* For printf()... */
#include <stdint.h> /* For uintmax_t... */
#include <math.h> /* For sqrt() .... */
int IntLog2(uintmax_t n) {
if (n == 0) return -1; /* Error */
int L;
for (L = 0; n >>= 1; L++)
;
return L; /* It takes < 64 steps for long long */
}
uintmax_t IntExp2(int n) {
if (n < 0)
return 0; /* Error */
uintmax_t E;
for (E = 1; n-- > 0; E <<= 1)
;
return E; /* It takes < 64 steps for long long */
}
uintmax_t IntSqrtDn(uintmax_t n) { return IntExp2(IntLog2(n) / 2); }
uintmax_t IntSqrtUp(uintmax_t n) { return IntExp2(IntLog2(n) / 2 + 1); }
int main(void) {
uintmax_t N = 947612934; /* Try here your number! */
uintmax_t sqrtn = IntSqrtDn(N), /* 1st approx. to sqrt(N) by below */
sqrtn0 = IntSqrtUp(N); /* 1st approx. to sqrt(N) by above */
/* The following means while( abs(sqrt-sqrt0) > 1) { stuff... } */
/* However, we take care of subtractions on unsigned arithmetic, just in case... */
while ( (sqrtn > sqrtn0 + 1) || (sqrtn0 > sqrtn+1) )
sqrtn0 = sqrtn, sqrtn = (sqrtn0 + N/sqrtn0) / 2; /* Newton iteration */
printf("N==%llu, sqrt(N)==%g, IntSqrtDn(N)==%llu, IntSqrtUp(N)==%llu, sqrtn==%llu, sqrtn*sqrtn==%llu\n\n",
N, sqrt(N), IntSqrtDn(N), IntSqrtUp(N), sqrtn, sqrtn*sqrtn);
return 0;
}
The last value stored in sqrtn is the integer square root of N.
The last line of the program just shows all the values, with comprobation purposes.
So, you can try different values of Nand see what happens.
If we add a counter inside the while-loop, we'll see that no more than a few iterations happen.
Remark: It is necessary to verify that the condition abs(sqrtn-sqrtn0)<=1 is always achieved when working in the integer-number setting. If not, we shall have to fix the algorithm.
Remark2: In the initialization sentences, observe that sqrtn0 == sqrtn * 2 == sqrtn << 1. This avoids us some calculations.
// sqrt_i64 returns the integer square root of v.
int64_t sqrt_i64(int64_t v) {
uint64_t q = 0, b = 1, r = v;
for( b <<= 62; b > 0 && b > r; b >>= 2);
while( b > 0 ) {
uint64_t t = q + b;
q >>= 1;
if( r >= t ) {
r -= t;
q += b;
}
b >>= 2;
}
return q;
}
The for loop may be optimized by using the clz machine code instruction.