質問

Sorry for unclear title, but I don't know how to state it properly (feel free to edit), so I will give example:

sqrt(108) ~ 10.39... BUT I want it to be like this sqrt(108)=6*sqrt(3) so it means expanding into two numbers

So that's my algorithm

i = floor(sqrt(number))                  //just in case, floor returns lowest integer value :)
while (i > 0)                            //in given example number 108
  if (number mod (i*i) == 0)
    first = i                            //in given example first is 6
    second = number / (i*i)              //in given example second is 3
    i = 0
  i--

Maybe you know better algorithm?

If it matters I will use PHP and of course I will use appropriate syntax

役に立ちましたか?

解決

There is no fast algorithm for this. It requires you to find all the square factors. This requires at least some factorizing.

But you can speed up your approach by quite a bit. For a start, you only need to find prime factors up to the cube root of n, and then test whether n itself is a perfect square using the advice from Fastest way to determine if an integer's square root is an integer.

Next speed up, work from the bottom factors up. Every time you find a prime factor, divide n by it repeatedly, accumulating out the squares. As you reduce the size of n, reduce your limit that you'll go to. This lets you take advantage of the fact that most numbers will be divisible by some small numbers, which quickly reduces the size of the number you have left to factor, and lets you cut off your search sooner.

Next performance improvement, start to become smarter about which numbers you do trial divisions by. For instance special case 2, then only test odd numbers. You've just doubled the speed of your algorithm again.

But be aware that, even with all of these speedups, you're just getting more efficient brute force. It is still brute force, and still won't be fast. (Though it will generally be much, much faster than your current idea.)

Here is some pseudocode to make this clear.

integer_sqrt = 1
remainder = 1

# First we special case 2.
while 0 == number % 4:
    integer_sqrt *= 2
    number /= 4

if 0 == number / 2:
    number /= 2
    remainder *= 2

# Now we run through the odd numbers up to the cube root.
# Note that beyond the cube root there is no way to factor this into
#    prime * prime * product_of_bigger_factors
limit = floor(cube_root(number + 1))
i = 3
while i <= limit:
    if 0 == number % i:
        while 0 == number % (i*i):
            integer_sqrt *= i
            number /= i*i
        if 0 == number % (i*i):
            number /= i
            remainder *= i
        limit = floor(cube_root(number + 1))
    i += 2

# And finally check whether we landed on the square of a prime.

possible_sqrt = floor(sqrt(number + 1))
if number == possible_sqrt * possible_sqrt:
    integer_sqrt *= possible_sqrt
else:
    remainder *= number

# And the answer is now integer_sqrt * sqrt(remainder)

Note that the various +1s are to avoid problems with the imprecision of floating point numbers.

Running through all of the steps of the algorithm for 2700, here is what happens:

number = 2700
integer_sqrt = 1
remainder = 1

enter while loop
    number is divisible by 4
        integer_sqrt *= 2 # now 2
        number /= 4 # now 675

    number is not divisible by 4
        exit while loop

number is not divisible by 2

limit = floor(cube_root(number + 1)) # now 8
i = 3
enter while loop
    i < =limit # 3 < 8
        enter while loop
            number is divisible by i*i # 9 divides 675
                integer_sqrt *= 3 # now 6
                number /= 9 # now 75

            number is not divisible by i*i # 9 does not divide 75
                exit while loop

        i divides number # 3 divides 75
            number /= 3 # now 25
            remainder *= 3 # now 3

        limit = floor(cube_root(number + 1)) # now 2

    i += 2 # now 5

    i is not <= limit # 5 > 2
        exit while loop

possible_sqrt = floor(sqrt(number + 1)) # 5

number == possible_sqrt * possible_sqrt # 25 = 5 * 5
    integer_sqrt *= possible_sqrt # now 30

# and now answer is integer_sqrt * sqrt(remainder) ie 30 * sqrt(3)

他のヒント

  1. List all prime divisors in increasing order e.g. 2700 = 2*2*3*3*3*5*5. This is the slowest step and requires sqrt(N) operations.
  2. Create an accumulator (start with 1). Scan this list. For every pair of numbers, multiply the accumulator by (one of) them. So after scanning the list above, you get 2*3*5.
  3. Accumulator is your multiplier. The rest remains under square root.
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top