一个高效的算法计算出的任意大整数的整数的平方根(isqrt)整数、平方根、高效、算法

2023-09-11 23:08:44 作者:少年多梦,不多心

对于二郎的解决方案 C / C ++ ,到试验4 的下文。

整数的平方根

的整数的平方根的定义可以在这里找到

计算平方根方法

,它的位魔术可以在这里找到一个算法 An algorithm that does "bit magic" could be found here
isqrt(N) when erlang:is_integer(N), N >= 0 ->
    erlang:trunc(math:sqrt(N)).

问题

此实现使用的sqrt() C库函数,因此它不具有任意大的整数工作(注意,返回的结果不匹配输入。正确的答案应该是 12345678901234567890 ):

Problem

This implementation uses the sqrt() function from the C library, so it does not work with arbitrarily large integers (Note that the returned result does not match the input. The correct answer should be 12345678901234567890):

Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
1> erlang:trunc(math:sqrt(12345678901234567890 * 12345678901234567890)).
12345678901234567168
2> 

[试验2:使用BIGINT + 仅]

code

[ Trial 2 : Using Bigint + Only ]

Code

isqrt2(N) when erlang:is_integer(N), N >= 0 ->
    isqrt2(N, 0, 3, 0).

isqrt2(N, I, _, Result) when I >= N ->
    Result;

isqrt2(N, I, Times, Result) ->
    isqrt2(N, I + Times, Times + 2, Result + 1).

说明

个人能力的计算公式

此实现是基于以下观察:

Description

This implementation is based on the following observation:

isqrt(0) = 0   # <--- One 0
isqrt(1) = 1   # <-+
isqrt(2) = 1   #   |- Three 1's
isqrt(3) = 1   # <-+
isqrt(4) = 2   # <-+
isqrt(5) = 2   #   |
isqrt(6) = 2   #   |- Five 2's
isqrt(7) = 2   #   |
isqrt(8) = 2   # <-+
isqrt(9) = 3   # <-+
isqrt(10) = 3  #   |
isqrt(11) = 3  #   |
isqrt(12) = 3  #   |- Seven 3's
isqrt(13) = 3  #   |
isqrt(14) = 3  #   |
isqrt(15) = 3  # <-+
isqrt(16) = 4  # <--- Nine 4's
...

问题

本实施涉及的仅 BIGINT增加,所以我希望它跑得快。然而,当我喂它与 1111111111111111111111111111111111111111 * 1111111111111111111111111111111111111111 ,它似乎对我的(非常快)机永远运行。

Problem

This implementation involves only bigint additions so I expected it to run fast. However, when I fed it with 1111111111111111111111111111111111111111 * 1111111111111111111111111111111111111111, it seems to run forever on my (very fast) machine.

isqrt3(N) when erlang:is_integer(N), N >= 0 ->
    isqrt3(N, 1, N).

isqrt3(_N, Low, High) when High =:= Low + 1 ->
    Low;

isqrt3(N, Low, High) ->
    Mid = (Low + High) div 2,
    MidSqr = Mid * Mid,
    if
        %% This also catches N = 0 or 1
        MidSqr =:= N ->
            Mid;
        MidSqr < N ->
            isqrt3(N, Mid, High);
        MidSqr > N ->
            isqrt3(N, Low, Mid)
    end.

变型2(以上code修改,以使边界与中秋节+ 1或半山1反潜,参照的由维克拉姆铢答案)

Variant 2 (modified above code so that the boundaries go with Mid+1 or Mid-1 instead, with reference to the answer by Vikram Bhat)

isqrt3a(N) when erlang:is_integer(N), N >= 0 ->
    isqrt3a(N, 1, N).

isqrt3a(N, Low, High) when Low >= High ->
    HighSqr = High * High,
    if
        HighSqr > N ->
            High - 1;
        HighSqr =< N ->
            High
    end;

isqrt3a(N, Low, High) ->
    Mid = (Low + High) div 2,
    MidSqr = Mid * Mid,
    if
        %% This also catches N = 0 or 1
        MidSqr =:= N ->
            Mid;
        MidSqr < N ->
            isqrt3a(N, Mid + 1, High);
        MidSqr > N ->
            isqrt3a(N, Low, Mid - 1)
    end.

问题

现在它解决了在闪电般的速度的79位数字(即 1111111111111111111111111111111111111111 * 1111111111111111111111111111111111111111 ),结果立即显示。但是,它需要60秒(+ - 2秒)我的机器上求一百万(1,000,000)61位数字(即从 1000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000001000000 )。我想更快地做到这一点。

Problem

Now it solves the 79-digit number (namely 1111111111111111111111111111111111111111 * 1111111111111111111111111111111111111111) in lightening speed, the result is shown immediately. However, it takes 60 seconds (+- 2 seconds) on my machine to solve one million (1,000,000) 61-digit numbers (namely, from 1000000000000000000000000000000000000000000000000000000000000 to 1000000000000000000000000000000000000000000000000000001000000). I would like to do it even faster.

isqrt4(0) -> 0;

isqrt4(N) when erlang:is_integer(N), N >= 0 ->
    isqrt4(N, N).

isqrt4(N, Xk) ->
    Xk1 = (Xk + N div Xk) div 2,
    if
        Xk1 >= Xk ->
            Xk;
        Xk1 < Xk ->
            isqrt4(N, Xk1)
    end.

code在C / C ++(您的关注)

递归变体

#include <stdint.h>

uint32_t isqrt_impl(
    uint64_t const n,
    uint64_t const xk)
{
    uint64_t const xk1 = (xk + n / xk) / 2;
    return (xk1 >= xk) ? xk : isqrt_impl(n, xk1);
}

uint32_t isqrt(uint64_t const n)
{
    if (n == 0) return 0;
    if (n == 18446744073709551615ULL) return 4294967295U;
    return isqrt_impl(n, n);
}

迭代变

#include <stdint.h>

uint32_t isqrt_iterative(uint64_t const n)
{
    uint64_t xk = n;
    if (n == 0) return 0;
    if (n == 18446744073709551615ULL) return 4294967295U;
    do
    {
        uint64_t const xk1 = (xk + n / xk) / 2;
        if (xk1 >= xk)
        {
            return xk;
        }
        else
        {
            xk = xk1;
        }
    } while (1);
}

问题

在二郎code解决了一个百万(1,000,000)61位数字在40秒内(+ - 1秒)我的机器上,所以这是不是的试用3 的速度更快。它可以去得更快?

Problem

The Erlang code solves one million (1,000,000) 61-digit numbers in 40 seconds (+- 1 second) on my machine, so this is faster than Trial 3. Can it go even faster?

处理器: 3.4 GHz的英特尔酷睿i7

Processor : 3.4 GHz Intel Core i7

内存: 32 GB 1600 MHz的DDR3

Memory : 32 GB 1600 MHz DDR3

OS:的Mac OS X版本10.9.1

OS : Mac OS X Version 10.9.1

整数平方根蟒蛇

借助通过user448810 使用牛顿法的答案。我不知道是否使用整数除法做除法是正常与否。我会尝试这后来成为一个更新。 [更新(2015年1月11日):这是正常的话]

The answer by user448810 uses "Newton's Method". I'm not sure whether doing the division using "integer division" is okay or not. I'll try this later as an update. [UPDATE (2015-01-11): It is okay to do so]

通过数学涉及使用第三方Python包 gmpy ,这是不是很利于我,因为我主要是在只有内置设施解决它在二郎感兴趣。

The answer by math involves using a 3rd party Python package gmpy, which is not very favourable to me, since I'm primarily interested in solving it in Erlang with only builtin facilities.

由DSM 似乎有趣的答案。我真的不明白是怎么回事,但似乎位魔术参与那里,所以它不是很适合我。

The answer by DSM seems interesting. I don't really understand what is going on, but it seems that "bit magic" is involved there, and so it's not quite suitable for me too.

无限递归元整数方根

这个问题是C ++,算法由阿拉克(提问者)看起来就像是从同一个想法的试验2 的上面。 This question is for C++, and the algorithm by AraK (the questioner) looks like it's from the same idea as Trial 2 above.

推荐答案

如何像下面不需要浮动部门仅整数乘法(比牛顿法慢)的二进制搜索: -

How about binary search like following doesn't need floating divisions only integer multiplications (Slower than newtons method) :-

low = 1;

/* More efficient bound

high = pow(10,log10(target)/2+1);

*/


high = target


while(low<high) {

 mid = (low+high)/2;
 currsq = mid*mid;

 if(currsq==target) {
    return(mid);
 }

 if(currsq<target) {

      if((mid+1)*(mid+1)>target) {
             return(mid);
      }    
      low =  mid+1;
  }

 else {

     high = mid-1;
 }

}

这适用于 O(logN)的迭代所以不应该,即使非常大的数字永远运行下去

This works for O(logN) iterations so should not run forever for even very large numbers

LOG10(目标)的计算,如果需要: -

acc = target

log10 = 0;

while(acc>0) {

  log10 = log10 + 1;
  acc = acc/10;
}

注意: ACC / 10 是整数除法

编辑: -

高效结合: - 的的sqrt(N)的数字为N大约一半的数量,所以你可以通过高= 10 ^(LOG10(N)/ 2 + 1)&功放;&安培; 低= 10 ^(LOG10(N)/ 2-1)来获得更紧密的结合,它应该提供2倍加速。

Efficient bound :- The sqrt(n) has about half the number of digits as n so you can pass high = 10^(log10(N)/2+1) && low = 10^(log10(N)/2-1) to get tighter bound and it should provide 2 times speed up.

评估的约束: -

bound = 1;
acc = N;
count = 0;
while(acc>0) {

 acc = acc/10;

 if(count%2==0) {

    bound = bound*10;
 }

 count++;

}

high = bound*10;
low = bound/10;
isqrt(N,low,high);