Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > What's the correct and fast way to determine if a (gig) number is a perfect square?

Reply
Thread Tools

What's the correct and fast way to determine if a (gig) number is a perfect square?

 
 
Sam Kong
Guest
Posts: n/a
 
      02-11-2007
Hello,

I'm solving a math problem in Ruby.
I need to determine if a number is a perfect square.
If the number is small, you may do like the following.

def perfect_square? n
sqrt = n ** 0.5
sqrt - sqrt.to_i == 0
end

But Float number has limitation on precision.
Thus the function won't work correctly for big numbers like
(123456789123456789).

How would you solve such a case?
It should be fast as well as correct because I will use it repeatedly.

Thanks in advance.

Sam

 
Reply With Quote
 
 
 
 
Joel VanderWerf
Guest
Posts: n/a
 
      02-11-2007
Sam Kong wrote:
> Hello,
>
> I'm solving a math problem in Ruby.
> I need to determine if a number is a perfect square.
> If the number is small, you may do like the following.
>
> def perfect_square? n
> sqrt = n ** 0.5
> sqrt - sqrt.to_i == 0
> end
>
> But Float number has limitation on precision.
> Thus the function won't work correctly for big numbers like
> (123456789123456789).
>
> How would you solve such a case?
> It should be fast as well as correct because I will use it repeatedly.


Easy: compare integers rather than floats.

x = 123456789123456789

sqrt = Math::sqrt(x)
p(x == sqrt.floor**2)

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

 
Reply With Quote
 
 
 
 
Xavier Noria
Guest
Posts: n/a
 
      02-11-2007
On Feb 11, 2007, at 8:30 PM, Sam Kong wrote:

> Hello,
>
> I'm solving a math problem in Ruby.
> I need to determine if a number is a perfect square.
> If the number is small, you may do like the following.
>
> def perfect_square? n
> sqrt = n ** 0.5
> sqrt - sqrt.to_i == 0
> end
>
> But Float number has limitation on precision.
> Thus the function won't work correctly for big numbers like
> (123456789123456789).
>
> How would you solve such a case?
> It should be fast as well as correct because I will use it repeatedly.


There are faster algorithms based on the fact that most non-squares
aren't quadratic residues modulo some integers. I remember some is
explained in Henri Cohen's "A Course in Computational Algebraic
Number Theory", but do not have the book at hand.

Perhaps you can take a look at the function that implements this test
in GNU MP, explained here,

http://www.swox.com/gmp/manual/Perfe...Algorithm.html

or the one in Pari:

http://www.ufr-mi.u-bordeaux.fr/~belabas/pari/

-- fxn


 
Reply With Quote
 
Xavier Noria
Guest
Posts: n/a
 
      02-12-2007
On Feb 11, 2007, at 10:48 PM, Xavier Noria wrote:

> There are faster algorithms based on the fact that most non-squares
> aren't quadratic residues modulo some integers. I remember some is
> explained in Henri Cohen's "A Course in Computational Algebraic
> Number Theory", but do not have the book at hand.


I've been able to consult the book today. There is a specialised
algorithm to compute integer roots using integer arithmetic, and the
integer square test itself. Thus, they guarantee a correct result. I
attach them below translated to Ruby.

Some operations would be written using bitwise stuff, but anyway the
code performs very poorly (compared to Math::sqrt) according to the
benchmarks. If performance is important a solution in C would be
worth exploring.

-- fxn


# From Henri Cohen's "A Course in Computational Algebraic Number
# Theory".

# Algorithm 1.7.1 (Integer Square Root) Given a positive integer n,
# this algorithm computes the integer part of the square root of n,
# i.e. the number m such that m^2 <= n < (m + 1)^2.
def isqrt(n)
x = n
loop do
y = ((x + n/x)/2)
if y < x
x = y
else
return x
end
end
end

# Cache the squares modulus 11, 63, 64, and 65. This is used to check
# for non-squares, since a square is a square mod k for all k. The
# choice of these numbers is based on the probability that a non-square
# is a square mod any of them, which is 6/715, less than a 1%.
$squares = {}
[11, 63, 64, 65].each do |m|
$squares[m] = [false] * m
(0...(m/2)).each {|i| $squares[m][i**2 % m] = true}
end


# Algorithm 1.7.3 (Square Test). Given a positive integer n,
# this algorithm determines whether n is a square or not,
# and if it is, outputs the square root of n. We assume the
# precomputations made above.
def issquare(n)
return false unless $squares[64][n % 64]

r = n % 45045 # 45045 = 63*65*11
return false unless $squares[63][r % 63]
return false unless $squares[65][r % 65]
return false unless $squares[11][r % 11]

q = isqrt(n)
return q**2 == n ? q : false
end

require 'benchmark'

$r = 1000

# square of 32248581868698698768678697647823648238462348
$s =
10399710325421624583100997305362730327341924241351 3150637645310539211244
0875299413673104

# non-square, the previous number minus 1
$ns =
10399710325421624583100997305362730327341924241351 3150637645310539211244
0875299413673103

# Just for the sake of curiosity, since the code based on Math::sqrt
is not correct.
Benchmark.benchmark do |x|
x.report("builtin is square (true)") do
1.upto($r) do
sqrt = Math::sqrt($s)
$s == sqrt.floor**2
end
end
x.report("modular is square (true)") do
1.upto($r) do
issquare($s)
end
end
x.report("builtin is square (false)") do
1.upto($r) do
sqrt = Math::sqrt($ns)
$ns == sqrt.floor**2
end
end
x.report("modular is square (false)") do
1.upto($r) do
issquare($ns)
end
end
end

 
Reply With Quote
 
Sam Kong
Guest
Posts: n/a
 
      02-12-2007
Hi Joel,

On Feb 11, 11:37 am, Joel VanderWerf <v...@path.berkeley.edu> wrote:
> Sam Kong wrote:
> > Hello,

>
> > I'm solving a math problem in Ruby.
> > I need to determine if a number is a perfect square.
> > If the number is small, you may do like the following.

>
> > def perfect_square? n
> > sqrt = n ** 0.5
> > sqrt - sqrt.to_i == 0
> > end

>
> > But Float number has limitation on precision.
> > Thus the function won't work correctly for big numbers like
> > (123456789123456789).

>
> > How would you solve such a case?
> > It should be fast as well as correct because I will use it repeatedly.

>
> Easy: compare integers rather than floats.
>
> x = 123456789123456789
>
> sqrt = Math::sqrt(x)
> p(x == sqrt.floor**2)


Yes. Your approach is better than mine.
But it gives a wrong answer for big numbers like 55833579873437812.


>
> --
> vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407


Thanks anyway.

Sam

 
Reply With Quote
 
Sam Kong
Guest
Posts: n/a
 
      02-12-2007
Hi Xavier,

I really appreciate your kind help.
I put my comment inline.

On Feb 12, 10:32 am, Xavier Noria <f...@hashref.com> wrote:
> On Feb 11, 2007, at 10:48 PM, Xavier Noria wrote:
>
> > There are faster algorithms based on the fact that most non-squares
> > aren't quadratic residues modulo some integers. I remember some is
> > explained in Henri Cohen's "A Course in Computational Algebraic
> > Number Theory", but do not have the book at hand.

>
> I've been able to consult the book today. There is a specialised
> algorithm to compute integer roots using integer arithmetic, and the
> integer square test itself. Thus, they guarantee a correct result. I
> attach them below translated to Ruby.
>
> Some operations would be written using bitwise stuff, but anyway the
> code performs very poorly (compared to Math::sqrt) according to the
> benchmarks. If performance is important a solution in C would be
> worth exploring.
>
> -- fxn
>
> # From Henri Cohen's "A Course in Computational Algebraic Number
> # Theory".
>
> # Algorithm 1.7.1 (Integer Square Root) Given a positive integer n,
> # this algorithm computes the integer part of the square root of n,
> # i.e. the number m such that m^2 <= n < (m + 1)^2.
> def isqrt(n)
> x = n
> loop do
> y = ((x + n/x)/2)
> if y < x
> x = y
> else
> return x
> end
> end
> end


Actually you posted this message, I implemented an algorithm myself.
It's just the way we calculate sqrt with pen and paper.
It might be slow but correct even for big numbers.

def max_digit n, m
result = 0
(0..9).each do |i|
break if ((n * 10) + i) * i > m
result = i
end
result
end

def perfect_square? n
arr = []
a = n
while true
a, b = a / 100, a % 100
arr.unshift b
break if a == 0
end
remain = 0
carried = 0
arr.each do |i|
num = remain * 100 + i
digit = max_digit(carried, num)
remain = num - (carried * 10 + digit) * digit
carried = (carried * 10 + digit) + digit
end
remain == 0
end

I felt ashamed to see the code you posted.OTL

>
> # Cache the squares modulus 11, 63, 64, and 65. This is used to check
> # for non-squares, since a square is a square mod k for all k. The
> # choice of these numbers is based on the probability that a non-square
> # is a square mod any of them, which is 6/715, less than a 1%.
> $squares = {}
> [11, 63, 64, 65].each do |m|
> $squares[m] = [false] * m
> (0...(m/2)).each {|i| $squares[m][i**2 % m] = true}
> end
>
> # Algorithm 1.7.3 (Square Test). Given a positive integer n,
> # this algorithm determines whether n is a square or not,
> # and if it is, outputs the square root of n. We assume the
> # precomputations made above.
> def issquare(n)
> return false unless $squares[64][n % 64]
>
> r = n % 45045 # 45045 = 63*65*11
> return false unless $squares[63][r % 63]
> return false unless $squares[65][r % 65]
> return false unless $squares[11][r % 11]
>
> q = isqrt(n)
> return q**2 == n ? q : false
> end
>
> require 'benchmark'
>
> $r = 1000
>
> # square of 32248581868698698768678697647823648238462348
> $s =
> 10399710325421624583100997305362730327341924241351 3150637645310539211244
> 0875299413673104
>
> # non-square, the previous number minus 1
> $ns =
> 10399710325421624583100997305362730327341924241351 3150637645310539211244
> 0875299413673103
>
> # Just for the sake of curiosity, since the code based on Math::sqrt
> is not correct.
> Benchmark.benchmark do |x|
> x.report("builtin is square (true)") do
> 1.upto($r) do
> sqrt = Math::sqrt($s)
> $s == sqrt.floor**2
> end
> end
> x.report("modular is square (true)") do
> 1.upto($r) do
> issquare($s)
> end
> end
> x.report("builtin is square (false)") do
> 1.upto($r) do
> sqrt = Math::sqrt($ns)
> $ns == sqrt.floor**2
> end
> end
> x.report("modular is square (false)") do
> 1.upto($r) do
> issquare($ns)
> end
> end
> end


Thank you for your support.

Sam


 
Reply With Quote
 
Joel VanderWerf
Guest
Posts: n/a
 
      02-12-2007
Sam Kong wrote:
> Hi Joel,
>
> On Feb 11, 11:37 am, Joel VanderWerf <v...@path.berkeley.edu> wrote:
>> Sam Kong wrote:
>>> Hello,
>>> I'm solving a math problem in Ruby.
>>> I need to determine if a number is a perfect square.
>>> If the number is small, you may do like the following.
>>> def perfect_square? n
>>> sqrt = n ** 0.5
>>> sqrt - sqrt.to_i == 0
>>> end
>>> But Float number has limitation on precision.
>>> Thus the function won't work correctly for big numbers like
>>> (123456789123456789).
>>> How would you solve such a case?
>>> It should be fast as well as correct because I will use it repeatedly.

>> Easy: compare integers rather than floats.
>>
>> x = 123456789123456789
>>
>> sqrt = Math::sqrt(x)
>> p(x == sqrt.floor**2)

>
> Yes. Your approach is better than mine.
> But it gives a wrong answer for big numbers like 55833579873437812.


Wrong how?

irb(main):001:0> x = 55833579873437812
=> 55833579873437812
irb(main):002:0> sqrt = Math::sqrt(x)
=> 236291303.0
irb(main):003:0> sqrt.floor**2 - x
=> -3

Ok, I can see that one problem with my approach is that I should have
used #round instead of #floor.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

 
Reply With Quote
 
Sam Kong
Guest
Posts: n/a
 
      02-12-2007


On Feb 12, 12:12 pm, Joel VanderWerf <v...@path.berkeley.edu> wrote:
> Sam Kong wrote:
> > Hi Joel,

>
> > On Feb 11, 11:37 am, Joel VanderWerf <v...@path.berkeley.edu> wrote:
> >> Sam Kong wrote:
> >>> Hello,
> >>> I'm solving a math problem in Ruby.
> >>> I need to determine if a number is a perfect square.
> >>> If the number is small, you may do like the following.
> >>> def perfect_square? n
> >>> sqrt = n ** 0.5
> >>> sqrt - sqrt.to_i == 0
> >>> end
> >>> But Float number has limitation on precision.
> >>> Thus the function won't work correctly for big numbers like
> >>> (123456789123456789).
> >>> How would you solve such a case?
> >>> It should be fast as well as correct because I will use it repeatedly.
> >> Easy: compare integers rather than floats.

>
> >> x = 123456789123456789

>
> >> sqrt = Math::sqrt(x)
> >> p(x == sqrt.floor**2)

>
> > Yes. Your approach is better than mine.
> > But it gives a wrong answer for big numbers like 55833579873437812.

>
> Wrong how?
>
> irb(main):001:0> x = 55833579873437812
> => 55833579873437812
> irb(main):002:0> sqrt = Math::sqrt(x)
> => 236291303.0
> irb(main):003:0> sqrt.floor**2 - x
> => -3
>
> Ok, I can see that one problem with my approach is that I should have
> used #round instead of #floor.


I think even if you use #round, the problem won't go away.
Float type cannot generate correct result due to its limited
precision.

Sam

>
> --
> vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407



 
Reply With Quote
 
Phrogz
Guest
Posts: n/a
 
      02-12-2007
On Feb 12, 2:07 pm, "Sam Kong" <sam.s.k...@gmail.com> wrote:
> On Feb 12, 12:12 pm, Joel VanderWerf <v...@path.berkeley.edu> wrote:
> > Wrong how?

>
> > irb(main):001:0> x = 55833579873437812
> > => 55833579873437812
> > irb(main):002:0> sqrt = Math::sqrt(x)
> > => 236291303.0
> > irb(main):003:0> sqrt.floor**2 - x
> > => -3

>
> I think even if you use #round, the problem won't go away.
> Float type cannot generate correct result due to its limited
> precision.


For example:
irb(main):004:0>
x=981723462487562983749812734972342334123435635465 656432452
=> 98172346248756298374981273497234233412343563546565 6432452

irb(main):005:0> y=x**2
=>
96378095679856948493754946318049293808086637413854 24520224844154985436178651763483837924660159303671 23924038732304

irb(main):006:0> z=Math.sqrt(y)
=> 9.81723462487563e+056

irb(main):007:0> z==x
=> true

irb(main):010:0> z==(x+1000)
=> true

irb(main):011:0> z==(x+10000)
=> true

 
Reply With Quote
 
Joel VanderWerf
Guest
Posts: n/a
 
      02-12-2007
Sam Kong wrote:
>
> On Feb 12, 12:12 pm, Joel VanderWerf <v...@path.berkeley.edu> wrote:
>> Sam Kong wrote:
>>> Hi Joel,
>>> On Feb 11, 11:37 am, Joel VanderWerf <v...@path.berkeley.edu> wrote:
>>>> Sam Kong wrote:
>>>>> Hello,
>>>>> I'm solving a math problem in Ruby.
>>>>> I need to determine if a number is a perfect square.
>>>>> If the number is small, you may do like the following.
>>>>> def perfect_square? n
>>>>> sqrt = n ** 0.5
>>>>> sqrt - sqrt.to_i == 0
>>>>> end
>>>>> But Float number has limitation on precision.
>>>>> Thus the function won't work correctly for big numbers like
>>>>> (123456789123456789).
>>>>> How would you solve such a case?
>>>>> It should be fast as well as correct because I will use it repeatedly.
>>>> Easy: compare integers rather than floats.
>>>> x = 123456789123456789
>>>> sqrt = Math::sqrt(x)
>>>> p(x == sqrt.floor**2)
>>> Yes. Your approach is better than mine.
>>> But it gives a wrong answer for big numbers like 55833579873437812.

>> Wrong how?
>>
>> irb(main):001:0> x = 55833579873437812
>> => 55833579873437812
>> irb(main):002:0> sqrt = Math::sqrt(x)
>> => 236291303.0
>> irb(main):003:0> sqrt.floor**2 - x
>> => -3
>>
>> Ok, I can see that one problem with my approach is that I should have
>> used #round instead of #floor.

>
> I think even if you use #round, the problem won't go away.
> Float type cannot generate correct result due to its limited
> precision.


I agree, but I don't think the problem shows up until you have much
larger numbers. Is that the case for your program?

In this case, 55833579873437812 cannot be exactly represented by a
float, but it doesn't matter for purposes of this calculation.

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Pythonic way to determine if a string is a number Roy Smith Python 6 02-17-2009 03:45 PM
Pythonic way to determine if a string is a number python@bdurham.com Python 3 02-17-2009 06:34 AM
Re: Pythonic way to determine if a string is a number python@bdurham.com Python 3 02-16-2009 05:37 AM
what is the better way to determine if a string is number or not? kathy C++ 2 02-02-2006 04:39 PM
Easier way to determine if a string is an alphanumeric number? fooboo C Programming 9 06-07-2005 10:02 PM



Advertisments
 



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