Реализации алгоритмов/Бинарный алгоритм вычисления НОД
Внешний вид
Реализации
[править]Цикл:
typedef unsigned int Integer; /* Вместо unsigned int можно подставить любой другой целочисленный тип */
Integer GCD (Integer a, Integer b) {
Integer shift;
/* НОД(0, x) = x */
if (!a)
return b;
/* НОД(x, 0) = x */
if (!b)
return a;
/* Вычисление shift = lg K, где K — наибольшая степень 2, на которую делятся без остатка a и b. */
for (shift = 0; !((a | b) & 1); ++shift) {
a >>= 1;
b >>= 1;
}
while (!(a & 1))
a >>= 1;
/* Начиная отсюда a всегда нечётно. */
do {
while (!(b & 1)) /* Цикл по X */
b >>= 1;
/* Теперь a и b нечётны, поэтому их разность чётна.
Вычисление a = min(a, b), b = (a - b) / 2. */
if (a < b)
b -= a;
else
a -= (b = a - b);
b >>= 1;
} while (b);
return a << shift;
}
Рекурсия:
static long gcd (long a, long b) {
if (a == 0)
return b; // НОД(0, b) = b
if (b == 0)
return a; // НОД(a, 0) = a
if (a == b)
return a; // НОД(a, a) = a
if (a == 1 || b == 1)
return 1; // НОД(1, b) = НОД(a, 1) = 1
if ((a & 1) == 0) // Если а — чётное, то…
return ((b & 1) == 0)
? gcd (a >> 1, b >> 1) << 1 // …если b — чётное, то НОД(a, b) = 2 * НОД(a / 2, b / 2)
: gcd (a >> 1, b); // …если b — нечётное, то НОД(a, b) = НОД(a / 2, b)
else // Если a — нечётное, то…
return ((b & 1) == 0)
? gcd (a, b >> 1) // …если b — чётное, то НОД(a, b) = НОД(a, b / 2)
: gcd (b, a > b ? a - b : b - a); // …если b — нечётное, то НОД(a, b) = НОД(b, |a - b|)
}
Без рекурсии:
function GCD (a, b) { // НОД двух целых чисел
var factor = 1;
while (true) {
// НОД(0, b) = b; НОД(a, 0) = a; НОД(a, a) = a;
if (a == b)
if (a == 0)
throw 'GCD(0, 0)'
else
return factor * a;
if (a == 0)
return factor * b;
if (b == 0)
return factor * a;
// НОД(1, b) = 1; НОД(a, 1) = 1;
if (a == 1 || b == 1)
return factor;
//Если a и b чётные, то НОД(a, b) = 2 * НОД(a / 2, b / 2);
if (!(a & 1) && !(b & 1)){
factor <<= 1;
a >>= 1;
b >>= 1;
}
// Если a чётное, b нечётное, то НОД(a, b) = НОД(a / 2, b);
else if (!(a & 1))
a >>= 1;
// Если b чётное, a нечётное, то НОД(a, b) = НОД(a, b / 2);
else if (!(b & 1))
b >>= 1;
// Если a и b нечётные и b > a, то НОД(a, b) = НОД((b - a) / 2, a);
else if (b > a)
b = (b - a) >> 1;
// Если a и b нечётные и b < a, то НОД(a, b) = НОД((a - b) / 2, b);
else
a = (a - b) >> 1;
}
}
Без рекурсии:
type IntType = Integer; { Вместо Integer можно подставить любой другой целочисленный тип }
function GCD(a, b: IntType): IntType;
var d: Integer;
begin
if a=0 then GCD:=b
else if b=0 then GCD:=a
else begin
d:=0;
while not Odd(a) and not Odd(b) do begin
Inc(d); a:=a shr 1; b:=b shr 1;
end;
while not Odd(a) do a:=a shr 1;
repeat
while not Odd(b) do b:=b shr 1;
if a<b then Dec(b,a)
else begin
b:=a-b; Dec(a,b);
end;
b:=b shr 1;
until b=0;
GCD:=a shl d;
end;
end;
Рекурсия:
type IntType = Integer; { Вместо Integer можно подставить любой другой целочисленный тип }
function GCD (a, b: IntType): IntType;
begin
if a = 0 then
GCD := b { НОД(0, b) = b }
else if b = 0 then
GCD := a { НОД(a, 0) = a }
else if a = b then
GCD := a { НОД(a, a) = a }
else if (a = 1) or (b = 1) then
GCD := 1 { НОД(1, b) = НОД(a, 1) = 1 }
else if Odd(a) then { Если а — нечётное, то… }
if Odd(b) then { …если b — нечётное, то… }
GCD := GCD(b, Abs(a - b)) { НОД(a, b) = НОД(b, |a - b|) }
else { …если b — чётное, то… }
GCD := GCD(a, b shr 1) { НОД(a, b) = НОД(a, b / 2) }
else { Если a — чётное… }
if Odd(b) then { …если b — нечётное, то… }
GCD := GCD(a shr 1, b) { НОД(a, b) = НОД(a / 2, b) }
else { …если b — чётное, то… }
GCD := GCD(a shr 1, b shr 1) shl 1 { НОД(a, b) = 2 * НОД(a / 2, b / 2) }
end;
Без рекурсии:
sub gcd
{
return $_[0] if $_[1] == 0;
return $_[1] if $_[0] == 0;
($u, $v)=@_; $g=1;$t=100;
while (1)
{
if ($u%2==1 || $v%2==1) {last;};
$u>>=1; ##(right shift)
$v>>=1;
$g<<=1; ## (left shift)
}
while ($u > 0)
{
if ($u%2==0 && $u>0) {$u = $u>>1;}
elsif ($v%2==0 && $v>0) {$v = $v>>1;}
else
{
$t = abs (($u-$v)/2);
if ($u < $v) {$v = $t;} else {$u = $t;}
}
}
return $v*$g;
}
Без рекурсии:
def binary_gcd(num1, num2):
shift = 0
# Если одно из чисел равно нулю, делитель - другое число
if num1 == 0:
return num2
if num2 == 0:
return num1
# Если num1 = 1010, а num2 = 0100, то num1 | num2 = 1110
# 1110 & 0001 == 0, тогда происходит сдвиг, который фиксируется в shift
while (num1 | num2) & 1 == 0:
shift += 1
num1 >>= 1
num2 >>= 1
#Если True, значит num1 - четное, иначе - нечетное
while num1 & 1 == 0:
# если нечетное, сдвигаем на один бит
num1 >>= 1
while num2 != 0:
# пока число нечётное, сдвигаем на один бит
while num2 & 1 == 0:
num2 >>= 1
# если первое число больше второго
if num1 > num2:
# меняем их местами
num1, num2 = num2, num1
#теперь первое число меньше второго, вычитаем
num2 -= num1
# возвращаем число, перед этим сдвинув его биты на shift
return num1 << shift