Реализации алгоритмов/Алгоритм Евклида: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Примеры на Бейсике; исправления и форматирование
Строка 1: Строка 1:
Далее приводятся реализации [[w:алгоритм Евклида|алгоритма Евклида]] для вычисления НОД на различных [[w:язык программирования|языках программирования]].
Реализации [[w:алгоритм Евклида|алгоритма Евклида]] для вычисления НОД — наибольшего общего делителя (англ. GCD — greatest common divisor) двух целых чисел на различных [[w:язык программирования|языках программирования]].

==[[w:Язык_ассемблера|Assembler]]==
===ARM===
Вычитание, без рекурсии:
<source lang="arm">
loop CMP Ri, Rj ; проверка условий NE (i != j), GT (i > j) и LT (i < j);
SUBGT Ri, Ri, Rj ; если GT, выполняется i = i-j;
SUBLT Rj, Rj, Ri ; если LT, выполняется j = j-i;
BNE loop ; если NE - переход на метку loop.
</source>

===Z80===
Вычитание, без рекурсии:
<source lang="z80">
GCD_DEHL: ; CALL Inputs: HL,DE; Output: DE
AND A ; сброс CF
LOOP:
SBC HL,DE ; совмещение трёх в одном - одного сравнения и поочередно двух вычитаний.
RET Z ; минимизация общего размера, поэтому в цикле.
JR NC,LOOP
ADD HL,DE ; откат лишнего вычитания
EX DE,HL
JR GCD_DEHL
</source>

==[[w:Бейсик|BASIC]]==
==[[w:Бейсик|BASIC]]==
===QBASIC, QuickBasic, VisualBasic (до версий с поддержкой .NET)===
Деление с остатком, без рекурсии:
Деление с остатком, без рекурсии:
===[[w:GW-BASIC|GW-BASIC]] и совместимые диалекты===
<source lang="basic">
<source lang="basic">
10 INPUT "Two integer numbers"; A%, B%
20 PRINT "GCD("; A%; ", "; B%; ") = ";
30 WHILE B% <> 0
40 A% = A% MOD B%
50 SWAP A%, B%
60 WEND
70 PRINT ABS(A%)
</source>

===[[w:QuickBasic|QuickBasic]] версий < 4.0, [[w:Turbo Basic|Turbo Basic]]===
<source lang="basic">
DEF FNGCD% (A%, B%)
DO WHILE B% <> 0
A% = A% MOD B%
SWAP A%, B%
LOOP
FNGCD% = ABS(A%)
END DEF
</source>

===[[w:PowerBASIC|PowerBASIC]], [[w:QBASIC|QBASIC]], [[w:QuickBasic|QuickBasic]], [[w:Visual Basic|Visual Basic]]===
<source lang="vb">
Function GCD (a As Integer, b As Integer) As Integer
Function GCD (a As Integer, b As Integer) As Integer
Do While a <> 0 And b <> 0
Dim d As Integer
Do While a <> 0 and b <> 0
If a > b Then
If a > b Then
a = a mod b
a = a Mod b
Else
Else
b = b mod a
b = b Mod a
End If
End If
Loop
Loop
GCD = Abs(a + b) ' Для VB.NET следует заменить эту строку на Return Math.Abs(a + b)
GCD = a + b
End Function
End Function
</source>
</source>


==[[w:C (язык программирования)|C]]==
==[[w:C (язык программирования)|C]]/[[w:C++|C++]]==
'''C:''' Тип T должен быть задан как:
Тип T определён как <code>typedef … T;</code>, вместо <code>…</code> следует подставить любой целочисленный тип (<code>int</code>, <code>unsigned</code>, <code>short</code> и т. д.)
<source lang="c">typedef … T;</source>, вместо <code>…</code> нужно подставить любой целочисленный тип (<code>int</code>, <code>unsigned</code>, <code>short</code> и т. д.)
'''C++:''' любую из нижеприведённых функций (включая <code>abs</code>) следует предварить строкой:
<source lang="cpp">template < typename T ></source>
Для корректной обработки отрицательных чисел все последующие примеры используется функция вычисления модуля числа:
<source lang="c">
T abs (T value) {
return (value < (T)0) ? -value : value;
}
</source>


Деление с остатком, без рекурсии:
Деление с остатком, без рекурсии:
Строка 29: Строка 84:
b = c;
b = c;
}
}
return (a < 0) ? -a : a;
return abs(a);
}
}
</source>
</source>
Строка 38: Строка 93:
while (b)
while (b)
b ^= a ^= b ^= a %= b;
b ^= a ^= b ^= a %= b;
return a;
return abs(a);
}
}
</source>
</source>
Строка 45: Строка 100:
<source lang="c">
<source lang="c">
T gcd (T a, T b) {
T gcd (T a, T b) {
return (b == 0) ? a : gcd(b, a % b);
return (b == 0) ? abs(a) : gcd(b, a % b);
}
}
</source>
</source>
Строка 52: Строка 107:
<source lang="c">
<source lang="c">
T gcd (T a, T b) {
T gcd (T a, T b) {
a = abs(a);
b = abs(b);
while (a != b) {
while (a != b) {
if (a > b)
if (a > b)
Строка 69: Строка 126:
while (b != 0)
while (b != 0)
b = a % (a = b);
b = a % (a = b);
return a;
return Math.Abs(a);
}
}
</source>
</source>
Строка 114: Строка 171:
b = c;
b = c;
}
}
return a;
return Math.abs(a);
}
}
</source>
</source>
Строка 123: Строка 180:
while (b != 0)
while (b != 0)
b ^= a ^= b ^= a %= b;
b ^= a ^= b ^= a %= b;
return a;
return Math.abs(a);
}
}
</source>
</source>
Строка 131: Строка 188:
<source lang="java">
<source lang="java">
public static <T> T gcd (T a, T b) {
public static <T> T gcd (T a, T b) {
return b == 0 ? a : gcd(b, a % b);
return b == 0 ? Math.abs(a) : gcd(b, a % b);
}
}
</source>
</source>
Строка 147: Строка 204:
else
else
b := b mod a;
b := b mod a;
GCD := a + b
GCD := Abs(a + b)
end;
end;
</source>
</source>
Строка 162: Строка 219:
b := c
b := c
end;
end;
GCD := a
GCD := Abs(a)
end;
end;
</source>
</source>
Строка 171: Строка 228:
begin
begin
if b = 0 then
if b = 0 then
GCD := a
GCD := Abs(a)
else
else
GCD := GCD(b, a mod b)
GCD := GCD(b, a mod b)
Строка 194: Строка 251:
else
else
$b = $b % $a;
$b = $b % $a;
$gcd = $a + $b;
}
}
return $gcd;
return abs($a + $b);
}
}
echo gcd(5, 3);
echo gcd(5, 3);
Строка 228: Строка 284:
while b:
while b:
a, b = b, a % b
a, b = b, a % b
return a
return abs(a)
</source>
</source>


Строка 234: Строка 290:
<source lang="python">
<source lang="python">
def gcd (a, b):
def gcd (a, b):
return a if b == 0 else gcd(b, a % b)
return abs(a) if b == 0 else gcd(b, a % b)
</source>
</source>


Строка 242: Строка 298:
def gcd (a, b)
def gcd (a, b)
a, b = b, a % b until b.zero?
a, b = b, a % b until b.zero?
a
a.abs
end
end
</source>
</source>
Строка 249: Строка 305:
<source lang="ruby">
<source lang="ruby">
def gcd (a, b)
def gcd (a, b)
return a if b.zero?
return a.abs if b.zero?
gcd(b, a % b)
gcd(b, a % b)
end
end
Строка 257: Строка 313:
<source lang="ruby">
<source lang="ruby">
def gcd (a, b)
def gcd (a, b)
a = a.abs
b = b.abs
if a > b
if a > b
a -= b
a -= b
Строка 277: Строка 335:
<source lang="bash">
<source lang="bash">
gcd () {
gcd () {
n = 1 a = $1 b = $2
n = 1 a = $1 b = $2
if [[ $a -ne 0 ]]
if [[ $a -ne 0 ]]
then
then
gcd $(( $b % $a )) $a
gcd $(( $b % $a )) $a
let "n = $?"
let "n = $?"
else
else
let "n = $b"
let "n = $b"
fi
fi
return $n
return $n
}
}


Строка 298: Строка 356:
УКАЗ
УКАЗ
ПОКА (a # 0) И (b # 0) ВЫП
ПОКА (a # 0) И (b # 0) ВЫП
ЕСЛИ a >= b ТО
ЕСЛИ a < b ТО
b := b ОСТАТОК a
ИНАЧЕ
a := a ОСТАТОК b
a := a ОСТАТОК b
ИНАЧЕ
b := b ОСТАТОК a
КОН
КОН
КОН;
КОН;
ВОЗВРАТ a + b
ВОЗВРАТ МОДУЛЬ(a + b)
КОН НОД;
КОН НОД;
</source>

==Ассемблер==
===ARM===
Вычитание, без рекурсии:
<source lang="arm">
loop CMP Ri, Rj ; проверка условий NE (i != j), GT (i > j) и LT (i < j);
SUBGT Ri, Ri, Rj ; если GT, выполняется i = i-j;
SUBLT Rj, Rj, Ri ; если LT, выполняется j = j-i;
BNE loop ; если NE - переход на метку loop.
</source>

===Z80===
Вычитание, без рекурсии:
<source lang="z80">
GCD_DEHL: ;CALL Inputs: HL,DE; Output: DE
AND A ;сброс CF
LOOP:
SBC HL,DE ;совмещение трёх в одном - одного сравнения и поочередно двух вычитаний.
RET Z ;минимизация общего размера, поэтому в цикле.
JR NC,LOOP
ADD HL,DE ;откат лишнего вычитания
EX DE,HL
JR GCD_DEHL
</source>
</source>



Версия от 04:58, 27 апреля 2017

Реализации алгоритма Евклида для вычисления НОД — наибольшего общего делителя (англ. GCD — greatest common divisor) двух целых чисел на различных языках программирования.

Assembler

ARM

Вычитание, без рекурсии:

loop	CMP Ri, Rj		; проверка условий NE (i != j), GT (i > j) и LT (i < j);
	SUBGT Ri, Ri, Rj	; если GT, выполняется i = i-j;
	SUBLT Rj, Rj, Ri	; если LT, выполняется j = j-i;
	BNE loop		    ; если NE - переход на метку loop.

Z80

Вычитание, без рекурсии:

GCD_DEHL:               ; CALL Inputs: HL,DE; Output: DE
        AND     A       ; сброс CF
LOOP:
        SBC     HL,DE   ; совмещение трёх в одном - одного сравнения и поочередно двух вычитаний.
        RET     Z       ; минимизация общего размера, поэтому в цикле.
        JR      NC,LOOP
        ADD     HL,DE   ; откат лишнего вычитания
        EX      DE,HL
        JR      GCD_DEHL

BASIC

Деление с остатком, без рекурсии:

GW-BASIC и совместимые диалекты

10 INPUT "Two integer numbers"; A%, B%
20 PRINT "GCD("; A%; ", "; B%; ") = ";
30 WHILE B% <> 0
40 A% = A% MOD B%
50 SWAP A%, B%
60 WEND
70 PRINT ABS(A%)

QuickBasic версий < 4.0, Turbo Basic

DEF FNGCD% (A%, B%)
    DO WHILE B% <> 0
        A% = A% MOD B%
        SWAP A%, B%
    LOOP
    FNGCD% = ABS(A%)
END DEF

PowerBASIC, QBASIC, QuickBasic, Visual Basic

Function GCD (a As Integer, b As Integer) As Integer
	Do While a <> 0 And b <> 0
		If a > b Then
			a = a Mod b
		Else
			b = b Mod a
		End If
	Loop
	GCD = Abs(a + b) ' Для VB.NET следует заменить эту строку на Return Math.Abs(a + b)
End Function

C/C++

C: Тип T должен быть задан как:

typedef  T;

, вместо нужно подставить любой целочисленный тип (int, unsigned, short и т. д.)

C++: любую из нижеприведённых функций (включая abs) следует предварить строкой:

template < typename T >

Для корректной обработки отрицательных чисел все последующие примеры используется функция вычисления модуля числа:

T abs (T value) {
    return (value < (T)0) ? -value : value;
}

Деление с остатком, без рекурсии:

T gcd (T a, T b) {
    T c;
    while (b) {
        c = a % b;
        a = b;
        b = c;
    }
    return abs(a);
}

Более короткое решение:

T gcd (T a, T b) {
    while (b)
        b ^= a ^= b ^= a %= b;
    return abs(a);
}

Деление с остатком, рекурсия:

T gcd (T a, T b) {
   return (b == 0) ? abs(a) : gcd(b, a % b);
}

Вычитание, без рекурсии:

T gcd (T a, T b) {
    a = abs(a);
    b = abs(b);
    while (a != b) {
        if (a > b)
            a -= b;
        else
            b -= a;
    }
    return a;
}

C#

Деление с остатком, без рекурсии:

static int GCD (int a, int b)
{
    while (b != 0)
        b = a % (a = b);
    return Math.Abs(a);
}

Erlang

Деление с остатком, рекурсия:

gcd(A, 0) -> A;
gcd(A, B) -> gcd(B, (A rem B)).

F#

Деление с остатком, рекурсия:

let rec gcd a b =
    match b with
    |0 -> a
    |b -> gcd b (a % b)

Forth (диалект RetroForth)

Деление с остатком, рекурсия:

: GCD ( n1 n2 -- n ) tuck mod 0; GCD ;

Haskell

Деление с остатком, рекурсия:

gcd :: Integral a => a -> a -> a
gcd 0 0 = error "НОД от 0 и 0 не определён."
gcd x y = gcd' (abs x) (abs y)
    where gcd' x 0 = x
          gcd' x y = gcd' y (x `rem` y)

Java

Деление с остатком, без рекурсии:

public static <T> T gcd (T a, T b) {
    while (b != 0) {
        T c = a % b;
        a = b;
        b = c;
    }
    return Math.abs(a);
}

Также допустимы функции, аналогичные написанным выше на C, например так:

public static <T> gcd (T a, T b) {
    while (b != 0)
        b ^= a ^= b ^= a %= b;
    return Math.abs(a);
}

*короткая запись работает не корректно. Например при начальных значениях 34 и 9 уже на следующем шаге значения получаются 43 и 0...

Деление с остатком, рекурсия:

public static <T> T gcd (T a, T b) {
    return b == 0 ? Math.abs(a) : gcd(b, a % b);
}

Pascal

Тип T определён как type T = …;, вместо следует подставить любой целочисленный тип (Integer, Byte, LongInt и т. д.)

Деление с остатком, без рекурсии:

function GCD (a, b: T): T;
begin
    while a * b <> 0 do
       if a > b then
           a := a mod b
       else
           b := b mod a;
    GCD := Abs(a + b)
end;

Более быстрый алгоритм:

function GCD (a, b: T): T;
var c: T;
begin
    while b > 0 do
    begin
        c := a mod b;
        a := b;
        b := c
    end;
    GCD := Abs(a)
end;

Деление с остатком, рекурсия:

function GCD (a, b: T): T;
begin
   if b = 0 then
       GCD := Abs(a)
   else
       GCD := GCD(b, a mod b)
end;

Perl

Деление с остатком, рекурсия:

sub gcd {
    return  $_[0] != 0  ?  gcd ( ( $_[1] % $_[0] ), $_[0] )  :  $_[1];
}

PHP

Деление с остатком, без рекурсии:

function gcd ($a, $b) {
    while ($a <> 0 && $b <> 0) {
        if ($a > $b)
            $a = $a % $b;
        else
            $b = $b % $a;
    }
    return abs($a + $b);
}
echo gcd(5, 3);

Prolog

Деление с остатком, рекурсия:

?GCD(a, b, x)

GCD(0, b, b) <-
GCD(a, 0, a) <-
GCD(a, b, x) <- a >= b, m is a mod b, GCD(m, b, x)
GCD(a, b, x) <- a < b, m is b mod a, GCD(a, m, x)

w:SWI-Prolog

Деление с остатком, рекурсия:

gcd(0, B, B).
gcd(A, 0, A).

gcd(A, B, X) :-  A >= B, M is A mod B, gcd(M, B, X).
gcd(A, B, X) :-  A < B, M is B mod A, gcd(A, M, X).

Python

Деление с остатком, без рекурсии:

def gcd (a, b):
    while b:
        a, b = b, a % b
    return abs(a)

Деление с остатком, рекурсия:

def gcd (a, b):
    return abs(a) if b == 0 else gcd(b, a % b)

Ruby

Деление с остатком, без рекурсии:

def gcd (a, b)
    a, b = b, a % b until b.zero?
    a.abs
end

Деление с остатком, рекурсия:

def gcd (a, b)
    return a.abs if b.zero?
    gcd(b, a % b)
end

Вычитание, без рекурсии:

def gcd (a, b)
    a = a.abs
    b = b.abs
    if a > b
        a -= b
    else
        b -= a
    end while a != b
    a
end

Scheme

Вычитание, рекурсия:

(define gcd (lambda (a b)
  (if (> a b) (gcd (- a b) b)
     (if (< a b) (gcd a (- b a))
        a))))

Shell

Деление с остатком, рекурсия:

gcd () {
    n = 1 a = $1 b = $2
    if [[ $a -ne 0 ]]
    then
        gcd $(( $b % $a )) $a
        let "n = $?"
    else
        let "n = $b"
    fi
    return $n
}

gcd $1 $2
echo "Greatest common divisor is $?"

Глагол

Деление с остатком, без рекурсии:

ЗАДАЧА НОД (a, b: ЦЕЛ): ЦЕЛ;
УКАЗ
    ПОКА (a # 0) И (b # 0) ВЫП
        ЕСЛИ a < b ТО
            b := b ОСТАТОК a
        ИНАЧЕ
            a := a ОСТАТОК b
        КОН
    КОН;
    ВОЗВРАТ МОДУЛЬ(a + b)
КОН НОД;

Программируемые микрокалькуляторы «Электроника»

Деление с остатком, без рекурсии. Корректно обрабатываются любые целые числа (включая 0 и отрицательные). В вычислениях участвуют только регистры стека.

Использование: <первое число> В↑ <второе число> В/О С/П (НОД на индикаторе).

МК-52 / 61 / 152 / 161

00. Fx≠0   01. 13     02. ↔      03. В↑     04. FВx    05. ÷      06. FВx    07. ↔      08. K[x]   09. ×
10. −      11. Fx=0   12. 02     13. +      14. K|x|   15. С/П

См. также