Основаная память

Память(запоминающее устройство) — это компонент компьютера, в котором хранятся программы и данные.

Бит

Основной единицей хранения данных в памяти является двоичный разряд, который называется битом. Бит может содержать 0 или 1. Эта самая маленькая единица памяти.

Существует двоичная и двоично-десятичная система счисления. Допустим, число 1944.

В двоичной системе счисления это 00000111100110000000011110011000

В двоично десятичной системе счисления 00010001 10011001 01000100 01000100

Суть в том, что для представления в двоично десятичной системе используются 4 бита для каждой цифры числа.

Оба числа содержат 16 бит, но разница в том, что с помощью двоичной системы таким колличеством бит можно представить 65 536 комбинаций(2162^{16}), а с помощью двоично десятичной 10 000(16/4=416/4=4 то есть только четырехзначные числа).

Адреса памяти

Память состоит из ячеек, каждая из которых может хранить некоторую информацию. Каждая ячейка имеет номер, который называется адресом. По адресу программы могут ссылаться на определенную ячейку. Если память содержит nn ячеек, то она имеет n1n-1 адресов. Все ячейки памяти содержат одинаковое число битов. Если ячейка состоит из k бит, она может содержать любую из 2k2^k комбинаций.

Соседние ячейки имеют последовательные адреса.

Если адрес состоит из m бит, максимальное число адресуемых ячеек составит 2m2^m Например, адрес для обращения к памяти (а) должен состоять по крайней мере из 4 бит(24=16>11<23=82^4=16>11<2^3=8), чтобы выражать все числа от 0 до 11. Для (б и в), достаточно 3-разрядного адреса(23=82^3=8). Число битов в адресе определяет максимальное количество адресуемых ячеек памяти.

Ячейка — минимальная адресуемая единица памяти. Байты($$байт(октет)=8бит) группируются в слова. Компьютер с 32(64)-разрядными словами имеет 4(8) байта на каждое слово. Такая единица, как слово, необходима, поскольку большинство команд производят операции над целыми словами (например, складывают два слова). Таким образом, 32-разрядная машина содержит 32-разрядные регистры и команды для манипуляций с 32-разрядными словами, тогда как 64-разрядная машина имеет 64-разрядные регистры и команды для перемещения, сложения, вычитания и других операций над 64-разрядными словами.

Упорядочение байтов

Байты в слове могут нумероваться слева на право и справа на лево.

В первой системе нумерация начинается с высшего порядкка, поэтому она относится к категории компьютеров с прямым порядком следования байтов(big endian), другая система с обратным порядком следования байтов(little endian).

В обеих системах 32-разрядное целое число (например, 6) представлено битами 110 в трех крайних правых битах слова, а остальные 29 бит представлены нулями. Если байты нумеруются слева направо, биты 110 находятся в байте 3 (или 7, или 11 и т. д.). Если байты нумеруются справа налево, биты 110 находятся в байте 0 (или 4, или 8 и т. д.). В обеих системах слово, содержащее это целое число, имеет адрес 0.

Если компьютеры содержат только целые числа, никаких сложностей не возникает. Однако многие прикладные задачи требуют использования цепочек символов и других типов данных.

Например, простая запись данных о персонале, состоящую из строки (имя сотрудника) и двух целых чисел (возраст и номер отдела). Строка завершается одним или несколькими нулевыми байтами, призванными заполнить слово целиком.

Для записи «Jim Smith, 21 год, отдел 260» (1×256+4=2601\times256 + 4 = 260) представлена схема

с нумерацией байтов справа налево

с нумерацией байтов слева направо

Предположим, что машина с нумерацией байтов слева направо пересылает запись на компьютер с нумерацией байтов справа налево по одному байту, начиная с байта 0 и заканчивая байтом 19. Будем считать, что биты не инвертируются при передаче, то есть байт 0 переносится из первой машины на вторую в байт 0 и т.д.

Компьютер, получивший запись, имя печатает правильно, но возраст получается 21×22421\times2^{24}, и номер отдела искажается. Такая ситуация возникает, поскольку при передаче записи порядок следования букв в слове меняется так, как нужно, но при этом порядок следования байтов в целых числах тоже меняется, что приводит к неверному результату.

Для решения проблемы нужно использовать программу, которая будет инвертировать байты в слове после создания копии.

Числа стали правильными, но строка превратилась в «MIJTIMS», при этом буква «H» расположилась отдельно. Строка переворачивается потому, что компьютер сначала считывает байт 0 (пробел), затем байт 1 (M) и т. д.

Простого решения не существует. Есть один способ, но он неэффективен. Нужно перед каждой единицей данных помещать заголовок, информирующий, какой тип данных следует за ним — строка, целое и т. д. Это позволит компьютеру-получателю производить только необходимые преобразования.

Код исправления ошибок

Чертов error-correcting code, мало что понятно, но кое-что понятно, перечитаю через какое-то время + можно воспользоваться этой статьей https://habr.com/ru/post/328202/

Корректирующие (или помехоустойчивые) коды — это коды, которые могут обнаружить и, если повезёт, исправить ошибки, возникшие при передаче данных. То есть, если вы поцарапали диск, то есть вероятность, что ошибок не будет(если царапина не в сантиметр толщиной).

Каналы с ошибкой

Разберёмся сперва, откуда вообще берутся ошибки, которые мы собираемся исправлять. Перед нами стоит следующая задача. Нужно передать несколько блоков данных, каждый из которых кодируется цепочкой двоичных цифр. Получившаяся последовательность нулей и единиц передаётся через канал связи. Но так сложилось, что реальные каналы связи часто подвержены ошибкам. Вообще говоря, ошибки могут быть разных видов — может появиться лишняя цифра или какая-то пропасть. Но мы будем рассматривать только ситуации, когда в канале возможны лишь замены нуля на единицу и наоборот. Причём опять же для простоты будем считать такие замены равновероятными.

Ошибка — это маловероятное событие (а иначе зачем нам такой канал вообще, где одни ошибки?), а значит, вероятность двух ошибок меньше, а трёх уже совсем мала. Мы можем выбрать для себя некоторую приемлемую величину вероятности, очертив границу «это уж точно невозможно». Это позволит нам сказать, что в канале возможно не более, чем kk ошибок. Это будет характеристикой канала связи.

Для простоты введём следующие обозначения. Пусть данные, которые мы хотим передавать, — это двоичные последовательности фиксированной длины. Чтобы не запутаться в нулях и единицах, будем иногда обозначать их заглавными латинскими буквами (A,B,C,A, B, C,\dots). Что именно передавать, в общем-то неважно, просто с буквами в первое время будет проще работать.

Кодирование и декодирование будем обозначать прямой стрелкой (\rightarrow), а передачу по каналу связи — волнистой стрелкой (\rightsquigarrow). Ошибки при передаче будем подчёркивать.

Например, пусть мы хотим передавать только сообщения A=0A=0 и B=1B=1. В простейшем случае их можно закодировать нулём и единицей (сюрприз!):

A0,B1.\begin{aligned} A &\to 0,\\ B &\to 1. \end{aligned}

Передача по каналу, в котором возникла ошибка будет записана так:

A01B.A \to 0 \rightsquigarrow \underline{1} \to B.

Цепочки нулей и единиц, которыми мы кодируем буквы, будем называть кодовыми словами. В данном простом случае кодовые слова — это 00 и 11.

Код с утроением

Давайте попробуем построить какой-то корректирующий код. Что мы обычно делаем, когда кто-то нас не расслышал? Повторяем дважды:

A00,B11.\begin{aligned} A &\to 00, \\ B &\to 11. \end{aligned}

Правда, это нам не очень поможет. В самом деле, рассмотрим канал с одной возможной ошибкой:

A0001?.A \to 00 \rightsquigarrow 0\underline{1} \to ?.

Какие выводы мы можем сделать, когда получили 0101? Понятно, что раз у нас не две одинаковые цифры, то была ошибка, но вот в каком разряде? Может, в первом, и была передана буква BB. А может, во втором, и была передана AA.

То есть, получившийся код обнаруживает, но не исправляет ошибки. Ну, тоже неплохо, в общем-то. Но мы пойдём дальше и будем теперь утраивать цифры.

A000,B111.\begin{aligned} A &\to 000, \\ B &\to 111. \end{aligned}

Проверим в деле:

A000010A?.A \to 000 \rightsquigarrow 0\underline{1}0 \to A?.

Получили 010010. Тут у нас есть две возможности: либо это BB и было две ошибки (в крайних цифрах), либо это AA и была одна ошибка. Вообще, вероятность одной ошибки выше вероятности двух ошибок, так что самым правдоподобным будет предположение о том, что передавалась именно буква AA. Хотя правдоподобное — не значит истинное, поэтому рядом и стоит вопросительный знак.

Если в канале связи возможна максимум одна ошибка, то первое предположение о двух ошибках становится невозможным и остаётся только один вариант — передавалась буква AA.

Про такой код говорят, что он исправляет одну ошибку. Две он тоже обнаружит, но исправит уже неверно.

Это, конечно, самый простой код. Кодировать легко, да и декодировать тоже. Ноликов больше — значит передавался ноль, единичек — значит единица.

Если немного подумать, то можно предложить код исправляющий две ошибки. Это будет код, в котором мы повторяем одиночный бит 5 раз.

Расстояние между кодами

Рассмотрим поподробнее код с утроением. Итак, мы получили работающий код, который исправляет одиночную ошибку. Но за всё хорошее надо платить: он кодирует один бит тремя. Не очень-то и эффективно.

И вообще, почему этот код работает? Почему нужно именно утраивать для устранения одной ошибки? Наверняка это всё неспроста.

Давайте подумаем, как этот код работает. Интуитивно всё понятно. Нолики и единички — это две непохожие последовательности. Так как они достаточно длинные, то одиночная ошибка не сильно портит их вид.

Пусть мы передавали 000000, а получили 001001. Видно, что эта цепочка больше похожа на исходные 000000, чем на 111111. А так как других кодовых слов у нас нет, то и выбор очевиден.

Но что значит «больше похоже»? А всё просто! Чем больше символов у двух цепочек совпадает, тем больше их схожесть. Если почти все символы отличаются, то цепочки «далеки» друг от друга.

Можно ввести некоторую величину d(α,β)d(\alpha, \beta), равную количеству различающихся цифр в соответствующих разрядах цепочек α\alpha и β\beta. Эту величину называют расстоянием Хэмминга. Чем больше это расстояние, тем меньше похожи две цепочки.

Например, d(010,010)=0d(010, 010) = 0, так как все цифры в соответствующих позициях равны, а вот d(010101,011011)=3d(010101, 011011) = 3.

Расстояние Хэмминга называют расстоянием неспроста. Ведь в самом деле, что такое расстояние? Это какая-то характеристика, указывающая на близость двух точек, и для которой верны утверждения:

  1. Расстояние между точками неотрицательно и равно нулю только, если точки совпадают.

  2. Расстояние в обе стороны одинаково.

  3. Путь через третью точку не короче, чем прямой путь.

Достаточно разумные требования.

Математически это можно записать так (нам это не пригодится, просто ради интереса посмотрим):

  1. d(x,y)0,d(x,y)=0x=y;d(x, y) \geqslant 0,\quad d(x, y) = 0 \Leftrightarrow x = y;

  2. d(x,y)=d(y,x);d(x, y) = d(y, x);

  3. d(x,z)+d(z,y)d(x,y)d(x, z) + d(z, y) \geqslant d(x, y)

Предлагаю читателю самому убедиться, что для расстояния Хэмминга эти свойства выполняются.

Окрестности

Таким образом, разные цепочки мы считаем точками в каком-то воображаемом пространстве, и теперь мы умеем находить расстояния между ними. Правда, если попытаться сколько нибудь длинные цепочки расставить на листе бумаги так, чтобы расстояния Хэмминга совпадали с расстояниями на плоскости, мы можем потерпеть неудачу. Но не нужно переживать. Всё же это особое пространство со своими законами. А слова вроде «расстояния» лишь помогают нам рассуждать.

Пойдём дальше. Раз мы заговорили о расстоянии, то можно ввести такое понятие как окрестность. Как известно, окрестность какой-то точки — это шар определённого радиуса с центром в ней. Шар? Какие ещё шары! Мы же о кодах говорим.

Но всё просто. Ведь что такое шар? Это множество всех точек, которые находятся от данной не дальше, чем некоторое расстояние, называемое радиусом. Точки у нас есть, расстояние у нас есть, теперь есть и шары.

Так, скажем, окрестность кодового слова 000000 радиуса 1 — это все коды, находящиеся на расстоянии не больше, чем 1 от него, то есть отличающиеся не больше, чем в одном разряде. То есть это коды:

{000,100,010,001}.\{000, 100, 010, 001\}.

Да, вот так странно выглядят шары в пространстве кодов.

А теперь посмотрите. Это же все возможные коды, которые мы получим в канале в одной ошибкой, если отправим 000000! Это следует прямо из определения окрестности. Ведь каждая ошибка заставляет цепочку измениться только в одном разряде, а значит удаляет её на расстояние 1 от исходного сообщения.

Аналогично, если в канале возможны две ошибки, то отправив некоторое сообщение xx, мы получим один из кодов, который принадлежит окрестности xx радиусом 2.

Тогда всю нашу систему декодирования можно построить так. Мы получаем какую-то цепочку нулей и единиц (точку в нашей новой терминологии) и смотрим, в окрестность какого кодового слова она попадает.

Сколько ошибок может исправить код?

Чтобы код мог исправлять больше ошибок, окрестности должны быть как можно шире. С другой стороны, они не должны пересекаться. Иначе если точка попадёт в область пересечения, непонятно будет, к какой окрестности её отнести.

В коде с удвоением между кодовыми словами 0000 и 1111 расстояние равно 2 (оба разряда различаются). А значит, если мы построим вокруг них шары радиуса 1, то они будут касаться. Это значит, точка касания будет принадлежать обоим шарам и непонятно будет, к какому из них её отнести.

Именно это мы и получали. Мы видели, что есть ошибка, но не могли её исправить.

Что интересно, точек касания в нашем странном пространстве у шаров две — это коды 0101 и 1010. Расстояния от них до центров равны единице. Конечно же, в обычно геометрии такое невозможно, поэтому рисунки — это просто условность для более удобного рассуждения.

В случае кода с утроением, между шарами будет зазор.

Минимальный зазор между шарами равен 1, так как у нас расстояния всегда целые (ну не могут же две цепочки отличаться в полутора разрядах).

В общем случае получаем следующее.

Этот очевидный результат на самом деле очень важен. Он означает, что код с минимальным кодовым расстоянием dmind_{min} будет успешно работать в канале с kk ошибками, если выполняется соотношение

dmin2k+1.d_{\min} \geqslant 2k+1.

Полученное равенство позволяет легко определить, сколько ошибок будет исправлять тот или иной код. А сколько код ошибок может обнаружить? Рассуждения такие же. Код обнаруживает kk ошибок, если в результате не получится другое кодовое слово. То есть, кодовые слова не должны находиться в окрестностях радиуса kk других кодовых слов. Математически это записывается так:

dmink+1.d_{\min}\geqslant k + 1.

Рассмотрим пример. Пусть мы кодируем 4 буквы следующим образом.

A10100,B01000,C00111,D11011.\begin{aligned} A \to 10100,\\ B \to 01000,\\ C \to 00111,\\ D \to 11011.\\ \end{aligned}

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

A

B

C

D

A

-

3

3

4

B

3

-

4

3

C

3

4

-

3

D

4

3

3

-

Минимальное расстояние dmin=3d_{min}=3, а значит 32k+13\geqslant2k+1, откуда получаем, что такой код может исправить до k=1k=1 ошибок. Обнаруживает же он две ошибки.

Рассмотрим пример:

A1010010110.A \to 10100 \rightsquigarrow 101\underline{1}0.

Чтобы декодировать полученное сообщение, посмотрим, к какому символу оно ближе всего.

A:d(10110,10100)=1,B:d(10110,01000)=4,C:d(10110,00111)=2,D:d(10110,11011)=3.\begin{aligned} A:\, d(10110, 10100) &= 1,\\ B:\, d(10110, 01000) &= 4,\\ C:\, d(10110, 00111) &= 2,\\ D:\, d(10110, 11011) &= 3. \end{aligned}

Минимальное расстояние получилось для символа AA, значит вероятнее всего передавался именно он:

A1010010110A?.A \to 10100 \rightsquigarrow 101\underline{1}0 \to A?.

Итак, этот код исправляет одну ошибку, как и код с утроением. Но он более эффективен, так как в отличие от кода с утроением здесь кодируется уже 4 символа.

Таким образом, основная проблема при построении такого рода кодов — так расположить кодовые слова, чтобы они были как можно дальше друг от друга, и их было побольше.

Для декодирования можно было бы использовать таблицу, в которой указывались бы все возможные принимаемые сообщения, и кодовые слова, которым они соответствуют. Но такая таблица получилась бы очень большой. Даже для нашего маленького кода, который выдаёт 5 двоичных цифр, получилось бы 25=322^5=32 варианта возможных принимаемых сообщений. Для более сложных кодов таблица будет значительно больше.

Попробуем придумать способ коррекции сообщения без таблиц. Мы всегда сможем найти полезное применение освободившейся памяти.

Интерлюдия: поле GF(2)

Для изложения дальнейшего материала нам потребуются матрицы. А при умножении матриц, как известно мы складываем и перемножаем числа. И тут есть проблема. Если с умножением всё более-менее хорошо, то как быть со сложением? Из-за того, что мы работаем только с одиночными двоичными цифрами, непонятно, как сложить 1 и 1, чтобы снова получилась одна двоичная цифра. Значит вместо классического сложения нужно использовать какое-то другое.

Введём операцию сложения как сложение по модулю 2 (хорошо известный программистам XOR):

0+0=0,0+1=1,1+0=1,1+1=0.\begin{aligned} 0 + 0 &= 0,\\ 0 + 1 &= 1,\\ 1 + 0 &= 1,\\ 1 + 1 &= 0. \end{aligned}

Умножение будем выполнять как обычно. Эти операции на самом деле введены не абы как, а чтобы получилась система, которая в математике называется полем. Поле — это просто множество (в нашем случае из 0 и 1), на котором так определены сложение и умножение, чтобы основные алгебраические законы сохранялись. Например, чтобы основные идеи, касающиеся матриц и систем уравнений по-прежнему были верны. А вычитание и деление мы можем ввести как обратные операции.

Множество из двух элементов {0,1}\{0, 1\} с операциями, введёнными так, как мы это сделали, называется полем Галуа GF(2). GF — это Galois field, а 2 — количество элементов.

У сложения есть несколько очень полезных свойств, которыми мы будем пользоваться в дальнейшем.

x+x=0x+x=0

Это свойство прямо следует из определения.

x+y=xy.x + y = x - y.

А в этом можно убедиться, прибавив yy к обеим частям равенства. Это свойство, в частности означает, что мы можем переносить в уравнении слагаемые в другую сторону без смены знака.

Проверяем корректность

Вернёмся к коду с утроением.

A000,B111.\begin{aligned} A &\to 000,\\ B &\to 111. \end{aligned}

Для начала просто решим задачу проверки, были ли вообще ошибки при передаче. Как видно, из самого кода, принятое сообщение будет кодовым словом только тогда, когда все три цифры равны между собой.

Пусть мы приняли вектор-строку xx из трёх цифр. (Стрелочки над векторами рисовать не будем, так как у нас почти всё — это вектора или матрицы.)

x=(x1,x2,x3).\dots \rightsquigarrow x = (x_1, x_2, x_3).

Математически равенство всех трёх цифр можно записать как систему:

{x1=x2,x2=x3.\left\{ \begin{aligned} x_1 &= x_2,\\ x_2 &= x_3. \end{aligned} \right.

Или, если воспользоваться свойствами сложения в GF(2), получаем

{x1+x2=0,x2+x3=0.\left\{ \begin{aligned} x_1 + x_2 &= 0,\\ x_2 + x_3 &= 0. \end{aligned} \right.

Или

{1x1+1x2+0x3=0,0x1+1x2+1x3=0.\left\{ \begin{aligned} 1\cdot x_1 + 1\cdot x_2 + 0\cdot x_3 &= 0,\\ 0\cdot x_1 + 1\cdot x_2 + 1\cdot x_3 &= 0. \end{aligned} \right.

В матричном виде эта система будет иметь вид

HxT=0Hx^T=0

где

H=(110011).H = \begin{pmatrix} 1 & 1 & 0\\ 0 & 1 & 1 \end{pmatrix}.

Транспонирование здесь нужно потому, что xx — это вектор-строка, а не вектор-столбец. Иначе мы не могли бы умножать его справа на матрицу.

Будем называть матрицу HH проверочной матрицей. Если полученное сообщение — это корректное кодовое слово (то есть, ошибки при передаче не было), то произведение проверочной матрицы на это сообщение будет равно нулевому вектору.

Умножение на матрицу — это гораздо более эффективно, чем поиск в таблице, но у нас на самом деле есть ещё одна таблица — это таблица кодирования. Попробуем от неё избавиться.

Кодирование

Итак, у нас есть система для проверки

{x1+x2=0,x2+x3=0.\left\{ \begin{aligned} x_1 + x_2 &= 0,\\ x_2 + x_3 &= 0. \end{aligned} \right.

Её решения — это кодовые слова. Собственно, мы систему и строили на основе кодовых слов. Попробуем теперь решить обратную задачу. По системе (или, что то же самое, по матрице HH) найдём кодовые слова.

Правда, для нашей системы мы уже знаем ответ, поэтому, чтобы было интересно, возьмём другую матрицу:

H=(101000110100011).H = \begin{pmatrix} 1 & 0 & 1 & 0 & 0 \\ 0 & 1 & 1 & 0 & 1\\ 0 & 0 & 0 & 1 & 1 \end{pmatrix}.

Соответствующая система имеет вид:

{x1+x3=0,x2+x3+x5=0,x4+x5=0.\left\{ \begin{aligned} x_1 + x_3 &= 0,\\ x_2 + x_3 + x_5 &= 0,\\ x_4 + x_5 &= 0. \end{aligned} \right.

Чтобы найти кодовые слова соответствующего кода нужно её решить.

В силу линейности сумма двух решений системы тоже будет решением системы. Это легко доказать. Если aa и bb — решения системы, то для их суммы верно

H(a+b)T=HaT+HbT=0+0=0,H(a+b)^T=Ha^T+Hb^T=0+0=0,

что означает, что она тоже — решение.

Поэтому если мы найдём все линейно независимые решения, то с их помощью можно получить вообще все решения системы. Для этого просто нужно найти их всевозможные суммы.

Выразим сперва все зависимые слагаемые. Их столько же, сколько и уравнений. Выражать надо так, чтобы справа были только независимые. Проще всего выразить x1,x2,x4x_1, x_2, x_4.

Если бы нам не так повезло с системой, то нужно было бы складывая уравнения между собой получить такую систему, чтобы какие-то три переменные встречались по одному разу. Ну, или воспользоваться методом Гаусса. Для GF(2) он тоже работает.

Итак, получаем:

{x1=x3,x2=x3+x5,x4=x5.\left\{ \begin{aligned} x_1 &= x_3,\\ x_2 &= x_3 + x_5,\\ x_4 &= x_5. \end{aligned} \right.

Чтобы получить все линейно независимые решения, приравниваем каждую из зависимых переменных к единице по очереди.

x3=1,x5=0:x1=1,x2=1,x4=0x(1)=(1,1,1,0,0),x3=0,x5=1:x1=0,x2=1,x4=1x(2)=(0,1,0,1,1).\begin{aligned} x_3=1, x_5=0:\quad x_1=1, x_2=1, x_4=0 \Rightarrow x^{(1)} = (1, 1, 1, 0, 0),\\ x_3=0, x_5=1:\quad x_1=0, x_2=1, x_4=1 \Rightarrow x^{(2)} = (0, 1, 0, 1, 1). \end{aligned}

Всевозможные суммы этих независимых решений (а именно они и будут кодовыми векторами) можно получить так:

a1x(1)+a2x(2),a_1 x^{(1)}+a_2 x^{(2)},

где a1,a2a_1, a_2 равны либо нулю или единице. Так как таких коэффициентов два, то всего возможно 22=42^2=4 сочетания.

Но посмотрите! Формула, которую мы только что получили — это же снова умножение матрицы на вектор.

(a1,a2)(1110001011)=aG.(a_1, a_2)\cdot \begin{pmatrix} 1 & 1 & 1 & 0 & 0 \\ 0 & 1 & 0 & 1 & 1 \end{pmatrix} = aG.

Строчки здесь — линейно независимые решения, которые мы получили. Матрица GG называется порождающей. Теперь вместо того, чтобы сами составлять таблицу кодирования, мы можем получать кодовые слова простым умножением на матрицу:

aaG.a \to aG.

Найдём кодовые слова для этого кода. (Не забываем, что длина исходных сообщений должна быть равна 2 — это количество найденных решений.)

0000000,0101011,1011100,1110111.\begin{aligned} 00 &\to 00000,\\ 01 &\to 01011,\\ 10 &\to 11100,\\ 11 &\to 10111. \end{aligned}

Итак, у нас есть готовый код, обнаруживающий ошибки. Проверим его в деле. Пусть мы хотим отправить 0101 и у нас произошла ошибка при передаче. Обнаружит ли её код?

a=01aG=01011x=01111HxT=(110)T0.a=01 \to aG=01011 \rightsquigarrow x=01\underline{1}11 \to Hx^T = (110)^T \neq 0.

А раз в результате не нулевой вектор, значит код заподозрил неладное. Провести его не удалось. Ура, код работает!

Для кода с утроением, кстати, порождающая матрица выглядит очень просто:

G=(111).G=\begin{pmatrix} 1&1&1 \end{pmatrix}.

Подобные коды, которые можно порождать и проверять матрицей называются линейными (бывают и нелинейные), и они очень широко применяются на практике. Реализовать их довольно легко, так как тут требуется только умножение на константную матрицу.

Ошибка по синдрому

Ну хорошо, мы построили код обнаруживающий ошибки. Но мы же хотим их исправлять!

Для начала введём такое понятие, как вектор ошибки. Это вектор, на который отличается принятое сообщение от кодового слова. Пусть мы получили сообщение xx, а было отправлено кодовое слово vv. Тогда вектор ошибки по определению

e=xv.e = x - v.

Но в странном мире GF(2), где сложение и вычитание одинаковы, будут верны и соотношения:

v=x+e,x=v+e.\begin{aligned} v &= x + e,\\ x &= v + e. \end{aligned}

В силу особенностей сложения, как читатель сам может легко убедиться, в векторе ошибки на позициях, где произошла ошибка будет единица, а на остальных ноль.

Как мы уже говорили раньше, если мы получили сообщение xx с ошибкой, то HxT0Hx^T\neq 0. Но ведь векторов, не равных нулю много! Быть может то, какой именно ненулевой вектор мы получили, подскажет нам характер ошибки?

Назовём результат умножения на проверочную матрицу синдромом:

s(x)=HxT.s(x)=Hx^T.

И заметим следующее

s(x)=HxT=H(v+e)T=HeT=s(e).s(x) = Hx^T = H(v+e)^T = He^T = s(e).

Это означает, что для ошибки синдром будет таким же, как и для полученного сообщения.

Разложим все возможные сообщения, которые мы можем получить из канала связи, по кучкам в зависимости от синдрома. Тогда из последнего соотношения следует, что в каждой кучке будут вектора с одной и той же ошибкой. Причём вектор этой ошибки тоже будет в кучке. Вот только как его узнать?

А очень просто! Помните, мы говорили, что у нескольких ошибок вероятность ниже, чем у одной ошибки? Руководствуясь этим соображением, наиболее правдоподобным будет считать вектором ошибки тот вектор, у которого меньше всего единиц. Будем называть его лидером.

Давайте посмотрим, какие синдромы дают всевозможные 5-элементные векторы. Сразу сгруппируем их и подчеркнём лидеров — векторы с наименьшим числом единиц.

s(x)s(x)

xx

000

00000,11100,01011,10111\underline{00000}, 11100, 01011, 10111

001

00010,11110,01001,10101\underline{00010}, 11110, 01001, 10101

010

01000,10100,00011,11111\underline{01000}, 10100, 00011, 11111

011

01010,10110,00001,1110101010, 10110, \underline{00001}, 11101

100

10000,01100,11011,00111\underline{10000}, 01100, 11011, 00111

101

10010,01110,11001,00101\underline{10010}, 01110, 11001, \underline{00101}

110

11000,00100,10011,0111111000, \underline{00100}, 10011, 01111

111

11010,00110,10001,0110111010, \underline{00110}, \underline{10001}, 01101

В принципе, для корректирования ошибки достаточно было бы хранить таблицу соответствия синдрома лидеру.

Обратите внимание, что в некоторых строчках два лидера. Это значит для для данного синдрома два паттерна ошибки равновероятны. Иными словами, код обнаружил две ошибки, но исправить их не может.

Лидеры для всех возможных одиночных ошибок находятся в отдельных строках, а значит код может исправить любую одиночную ошибку. Ну, что же… Попробуем в этом убедиться.

a=01aG=01011x=01111s(x)=HxT=(110)Te=(00100).a=01 \to aG=01011 \rightsquigarrow x=01\underline{1}11 \to s(x)=Hx^T = (110)^T \to e=(00100).

Вектор ошибки равен (00100)(00100), а значит ошибка в третьем разряде. Как мы и загадали.

Ура, всё работает!

Кэш-память

Процессоры работют быстрее память, из-за этого процессору нужно ждать несколько циклов прежде, чем он получит слово. Решение - помещать память на микросхему процессора.

Кэш-память - память с небольшим объемом и высокой скоростью.

В нем находятся слова, которые чаще всрего используются. Если процессору нужно какое-нибудь слово, сначала он обращается к кэш-памяти. Если слова там нет, он обращается к основной памяти. Если значительная часть слов находится в кэш-памяти, среднее время доступа значительно сокращается.

Таким образом, успех или неудача зависит от того, какая часть слов находится в кэш-памяти(будут ли нужные слова находиться в кэш-памяти).

Программы не обращаются к памяти наугад. Если программе нужен доступ к адресу А, то скорее всего после этого ей понадобится доступ к адресу, расположенному поблизости от А. Практически все команды обычной программы (за исключением команд перехода и вызова процедур) вызываются из последовательных областей памяти. Кроме того, большую часть времени программа тратит на циклы, когда ограниченный набор команд выполняется снова и снова.

Ситуация, когда при последовательных обращениях к памяти в течение некоторого промежутка времени используется только небольшая ее область, называется принципом локальности. Этот принцип составляет основу всех систем кэш-памяти. Идея состоит в том, что когда определенное слово вызывается из памяти, оно вместе с соседними словами переносится в кэш-память, что позволяет при очередном запросе быстро обращаться к следующим словам.

Если слово считывается или записывается kk раз, компьютеру требуется сделать одно обращение к медленной основной памяти и k1k — 1 обращений к быстрой кэш-памяти. Чем больше kk, тем выше общая производительность.

Далее идут серьезные вычисления которые я решил не конспектировать

Основная память и кэш-память делятся на блоки фиксированного размера с учетом принципа локальности. Блоки внутри кэш-памяти обычно называют строками кэша (cache lines). При кэш-промахе(случается когда нужного слова нет в кэше) из основной памяти в кэш-память загружается вся строка, а не только необходимое слово. Например, если строка состоит из 64 байт, обращение к адресу 260 влечет за собой загрузку в кэш-память всей строки (байты с 256 по 319).

Дальше идут 5 серьезных вопросов к кэшу.

Сборка модулей памяти и их типы

Раньше память из себя представляла отдельные микросхемы, но позже люди стали несколько микросхем(8 или 16) размещать на одной печатной плате, как один блок, он называется SIMM или DIIM

  • SIMM - Single Inline Memory Module — модуль памяти с односторонним расположением выводов. Устанавливается один краевой разъем с 72 контактами, скорость передачи за один тактовый цикл составляет 32 бита.

  • DIIM - Dual Inline Memory Module — модуль памяти с двухсторонним расположением выводов. Снабжается двумя краевыми разъемами(по одному на каждой стороне платы) контактами, т.о. общее кол-во контактов достигает 240(по 120), а скорость передачи данных возрастает до 64 бит за цикл.

В портативных компьютерах используется SO-DIIM(Small Outline DIMM)(DIIM меньшего размера). Модули SIMM и DIMM могут содержать бит четности или код исправления ошибок, поскольку вероятность возникновения ошибок в модуле составляет примерно одну ошибку за 10 лет, в большинстве обычных компьютеров схемы обнаружения и исправления ошибок не применяются.

Last updated