Московский государственный индустриальный университет
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3333 / 380 | Оценка: 4.17 / 3.79 | Длительность: 24:17:00
Специальности: Программист
Лекция 7:

Базисные схемы обработки информации

< Лекция 6 || Лекция 7: 12345 || Лекция 8 >

Схема вычисления инвариантной функции

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

Определение 7.3. Пусть X — некоторое множество, F\colon X\rightarrow
Y — заданная на нем функция, а Pпредикат такой, что P(x) = (F(x) легко вычислить ). Обозначим через X_P то подмножество множества X, где P(x)=T. Если существует преобразование T\colon X\setminus X_P\rightarrow X такое, что \forall x\in X\setminus X_P\ F(T(x))=F(x), то функция F называется T -инвариантной или просто инвариантной функцией.

Простейшим примером инвариантной функции является хорошо известная еще из средней школы функция f\colon \mathbb{R} \rightarrow
\mathbb{R}, f(x)=\sin x. Она является T -инвариантной относительно преобразования T\colon \mathbb{R} \rightarrow \mathbb{R}, T(x) =
x+2\pi.

Наибольший общий делитель двух целых чисел (greatest common divisor, gcd(x,y) или просто \mbox{НОД(x,y)} ) инвариантен относительно преобразования T\colon\mathbb{Z}\times \mathbb{Z} \rightarrow \mathbb{Z}\times
\mathbb{Z}, задаваемого формулой

T(x,y)=\begin{cases}
	(x-y,y),& \text{если $x\geqslant y$},\\
	(x,y-x),&  \text{иначе}.
	\end{cases}

Доказательство этого факта, основанное на основной теореме арифметики о разложении числа на простые множители, является достаточно простым и оставляется читателю. Обратите только внимание на то, что функция gcd(x,y) не определена в точке (0,0).

Для вычисления значения T -инвариантной функции F в точке x_0\in X применяется следующая схема.

Схема вычисления инвариантной функции.

Многократно выполняется преобразование T , дающее последовательность точек x_0, x_1, \ldots Если очередная точка x_n попадaет в подмножество X_P , то итерации завершаются. По определению инвариантной функции F(x_n) легко вычисляется и совпадает с искомым F(x_0).

Рисунок 7.4 содержит графическую иллюстрацию этой схемы.

Схема вычисления инвариантной функции

Рис. 7.4. Схема вычисления инвариантной функции

Схема вычисления инвариантной функции значительно облегчает проектирование программы "S0;while(e)S;S1;", так как нам изначально известны инвариант I = (F(x) = F(x_0)) и условие продолжения цикла e(x) =
!P(x). Тело цикла S конструируется, как программная реализация известного преобразования T, а написание S1, вычисляющей F(x_n), не может представлять трудностей в силу самого определения инвариантной функции.

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

Задача 7.4. Напишите программу, находящую наибольший общий делитель gcd(x,y) двух целых неотрицательных чисел x и y, не равных одновременно нулю. Воспользуйтесь следующими свойствами наибольшего общего делителя (не забудьте научиться доказывать все эти свойства):

  • gcd(x,y)=gcd(x,y-x)=gcd(x-y,y),
  • gcd(x,y)=gcd(x,y+x)=gcd(x+y,y),
  • gcd(x,x)=x, gcd(x,y)=gcd(y,x), gcd(x,0)=gcd(0,x)=x.

Решение Если через \mathbb{Z}_M^+ обозначить множество всех неотрицательных целых чисел, представимых в ЭВМ, то X = \mathbb{Z}_M^+\times
\mathbb{Z}_M^+
\setminus \{(0,0)\}, Y=\mathbb{Z}_M^+, X_P =
\{(x,y)\in X\ x=0 \lor
y=0\}, F(x,y) = gcd(x,y). В качестве преобразования T\colon
X\setminus X_P \rightarrow X можно взять

T(x,y)=\begin{cases}
	(x-y,y),& \text{если $x\geqslant y$},\\
	(x,y-x),&  \text{иначе}.
	\end{cases}

Таким образом, нам известны инвариант I=(gcd(x,y)=gcd(x_0,y_0)) и условие продолжения e=(x\ne 0\land y\ne 0). Начальные присваивания S0 в данном случае не нужны, тело цикла S пишется по определению T, a программа S1, вычисляющая gcd(x,y) для (x,y)\in
X_P, реализуется с помощью справедливой для этих значений аргумента формулы gcd(x,y)=x+y.

Текст программы

public class Gcd {
    public static void main(String[] args) throws Exception {
        int x = Xterm.inputInt("x -> ");
        int y = Xterm.inputInt("y -> ");
        Xterm.print("gcd(" + x + "," + y + ") =");
        while ( (x != 0) && (y != 0) ) {
            if (x >= y) x -= y;
            else        y -= x;
        } 
        Xterm.println(" " + (x+y));
    }
}

Обратите внимание на тот факт, что в построенной программе не понадобилось наличие переменной, соответствующей значению инвариантной функции gcd(x,y).

Рассмотрим еще одну задачу.

Задача 7.5. Напишите программу, перемножающую два целых числа, одно из которых неотрицательно, без использования операции умножения. Точные пред- и постусловия требуемой программы, временная сложность которой не должна превосходить \Theta(\log b), таковы: Q=(a\in \mathbb{Z}_M \land b\in \mathbb{Z}_M \land b \geqslant 0), R1=(z=ab). При написании программы величины a и b изменять не разрешается. Воспользуйтесь тем, что функция F\colon \mathbb{Z}_M\times\mathbb{Z}_M\times\mathbb{Z}_M
\rightarrow \mathbb{Z}_M, F(x,y,z) = z+xy является инвариантной относительно преобразования T\colon \mathbb{Z}_M\times\mathbb{Z}_M\times\mathbb{Z}_M
\rightarrow \mathbb{Z}_M\times\mathbb{Z}_M\times\mathbb{Z}_M, задаваемого формулой

T(x,y,z)=\begin{cases}
	(2x,y/2,z),& \text{если $y$ — четно},\\
	(x,y-1,z+x), & \text{иначе}.
	\end{cases}

В данном случае X=\mathbb{Z}_M\times\mathbb{Z}_M^+\times\mathbb{Z}_M, Y=\mathbb{Z}_M, P=(y=0), X_P = \{(x,y,z)\in X
\colon y=0\}. Функция F и преобразование T заданы в условии задачи.

Таким образом, нам известны инвариант I=(F(x,y,z)=F(a,b,0)) и условие продолжения e=(y\ne 0). Начальные присваивания S0 очевидны ( "x=a; y=b; z=0;" ), тело цикла S пишется по определению T, a программа S1, вычисляющая F(x,y,z) для (x,y,z)\in
X_P, в данном случае вырождается в пустой оператор ";", так как для этих значений аргумента F(x,y,z)=z. В результате получаем уже знакомую нам программу.

Текст программы

public class MulInv {
    public static void main(String[] args) throws Exception {
        int a = Xterm.inputInt("a -> ");
        int b = Xterm.inputInt("b -> ");
        int x = a, y = b, z = 0;
        while (y > 0) {
            if ((y&1) == 0) {
                y >>>= 1; x +=  x;
            } else {
                y  -=  1; z +=  x;
            }
        }
        Xterm.println("a * b = " + z);
    }
}
< Лекция 6 || Лекция 7: 12345 || Лекция 8 >
Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева