对于二郎的解决方案
或 C / C ++
,到试验4 的下文。
整数的平方根
的整数的平方根的定义可以在这里找到计算平方根方法
,它的位魔术可以在这里找到一个算法 An algorithm that does "bit magic" could be found hereisqrt(N) when erlang:is_integer(N), N >= 0 ->
erlang:trunc(math:sqrt(N)).
此实现使用的sqrt()
C库函数,因此它不具有任意大的整数工作(注意,返回的结果不匹配输入。正确的答案应该是 12345678901234567890
):
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>
+
仅]
+
Only ]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).
此实现是基于以下观察:
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
,它似乎对我的(非常快)机永远运行。
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.
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
)。我想更快地做到这一点。
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.
#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 的速度更快。它可以去得更快?
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);