Форум Академгородка, Новосибирск > Консультации по методам программирования
Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Консультации по методам программирования
Форум Академгородка, Новосибирск > Образование > Школьный форум > Форум ВКИ
fenster
Предлагаю выделить отдельную тему (эту) для вопросов и ответов по курсу методов программирования, чтобы не привязываться к предыдущему треду про "нужны лабы".

Скажу сразу, что выучивать алгоритмы -- занятие достаточно бессмысленное, алгоритмы нужно понимать. Если у вас это не получается -- для вас этот тред.

Задавайте ваши вопросы, господа студенты smile.gif

// в принципе, вопросы можно задавать по любой школьной математике, не только по методам программирования smile.gif
SupremEvil
Расскажите, пожалуйста алгоритм Бойера-Мура, этот алгоритм я не понимаю в принципе...уделите внимание, пожалуйста, описанию пострения таблицы смещений....
Amigos
QUOTE (SupremEvil @ Jun 11 2007, 23:09)
Расскажите, пожалуйста алгоритм Бойера-Мура, этот алгоритм я не понимаю в принципе...уделите внимание, пожалуйста, описанию пострения таблицы смещений....

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

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


Криво blink.gif , но, думаю, правильно ))


А теперь расскажите мне пожалуйста поподробнее алгоритмы пирамидальной сортировки и генерации перестановок по таблице инверсий ))
fenster
Пирамидальная сортировка

Попытаюсь изложить идею кратко, по пунктам. Даже вот рисунки нарисовал.

Пирамидой назовём такой массив A, в котором для каждого i выполняются условия A[i ] >= A[2*i+1] и A[i ] >= A[2*i+2] (кроме тех элементов, у которых нет парных). Я здесь считаю, что элементы нумеруются с нуля. Если мы каким-то волшебным образом сделали из массива пирамиду, то ясно, что максимальный элемент в таком массиве стоит в нулевой позиции. Можно сказать, что элементы массива выстроены в такое дерево, как на первом рисунке.

Научимся превращать массив в пирамиду. Во-первых, ясно, что у всех элементов правее середины (начиная с A[N/2+1]) парных элементов просто нет, т.е. для них условие пирамиды выполняется. На втором рисунке показан процесс просеивания -- в условии, что пирамида начинается от i+1-го элемента и продолжается до конца массива, к пирамиде добавляется i-й элемент. Делается это просто:

CODE
просеивание(A, i, N):
пока i < N, повторять:
   max = i
   если 2*i+1 < N и A[2*i+1] > A[max], присвоить max = 2*i+1
   если 2*i+2 < N и A[2*i+2] > A[max], присвоить max = 2*i+2
   // max -- номер максимального из трёх: A[i], A[2*i+1], A[2*i+2]
   если max == i, выход // условие пирамиды выполнено
   обмен A[i] <=> A[max]
   // при этом пирамида может сломаться в элементе A[max], ведь мы поменяли его на меньший
   i = max // поэтому перейдём к проверке max-го элемента


Теперь, собственно, сам алгоритм. Изначально у нас, как вы помните, пирамида в массиве начинается от середины. Поочерёдно просеивая все элементы от среднего до нулевого, добавляем их к пирамиде (как на рис. 2) и получаем массив, как на рис. 3 справа. Максимальный элемент у него в начале. Ставим его в конец массива, отсекаем конец (N--) и замечаем, что элементы от первого (не считая отсечённого последнего) всё ещё образуют пирамиду (см. на дерево на рис. 3). Чтобы очередной максимальный элемент опять вылез в начало, достаточно вызвать просеивание для нулевого элемента. Затем новый максимальный опять в конец, и так далее.

CODE
пирамидальная сортировка(A, N):
для i от N/2 до 0 выполнить
   просеивание(A, i, N)
пока N > 1, повторять
   обмен A[0] <=> A[N-1]
   N--
   просеивание(A, 0, N)


Алгоритм можно легко переписать на C -- размер программы будет ничуть не больше.

Время работы алгоритма: т.к. операция просеивания выполняет не более log N операций (по основанию 2), суммарная сложность -- O(N log N).
fenster
Генерация перестановок по таблице инверсий

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

Оказывается, таблицы инверсий проще перебирать, чем перестановки. Достаточно заметить, что последний элемент таблицы инверсий -- всегда 0, предпоследний -- 0 или 1, предпредпоследний -- 0, 1 или 2 и т.п. Начинаем от таблицы инверсий 000...0 и "добавляем" к ней по единице как будто бы "в столбик", но помня о максимально допустимых значениях. По каждой таблице инверсий восстанавливаем перестановку.

Пример (пишу от руки, надеюсь, без ошибок):

CODE
0000  => 1234
+  1
----
0010  => 1243
+  1
----
0100  => 1324
+  1
----
0110  => 1423
+  1
----
1000  => 2134
+  1
----
1010  => 2143
+  1
----
1100  => 3124
+  1
----
1110  => 4123
+  1
----
2000  => 2314
......
и т.п.
+  1
----
3210  => 4321


Вот так они все и перебираются.
fenster
По Бойеру-Муру есть вопросы? Amigos рассказал всё правильно.
fenster
Машинная память как однородный линейно-адресуемый массив ячеек фиксированной разрядности. Понятие разрядности и формата числа.

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

Любой тип (беззнаковое целое, знаковое целое, вещественные числа) занимают какое-то количество разрядов (бит), кратное восьми (1 байту), и записаны согласно некоторым правилам (формату). В курсе методов мы рассматривали следующие типы:

* целые числа без знака
* целые числа со знаком
* вещественные числа в формате с фиксированной точкой
* вещественные числа в формате с плавающей точкой
Akullina
не очень понимаю суть билета

36. Области существования данных, связанные с программой и процедурами. Правила видимости имён из разных областей.
GAVRICK
Александр Генадьевич обьясните этот билет

8. Проблема представления произвольного действительного числа в ЭВМ. Модель действительных чисел конечной точности: интервалы (классы чисел, сравнимых с заданной точностью). Арифметика интервалов. Понятие погрешности (потери точности).
fenster
Области существования данных, связанные с программой и процедурами. Правила видимости имён из разных областей.

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

CODE
int a; // глобальная переменная

void f()
{
   int a; // локальная переменная
   { // вложенный блок
       int a; // локальная переменная для блока
       a = 7; // присваивание локальной переменной блока
       ::a = 8; // присваивание глобальной переменной
       // здесь нет способа получить доступ к переменной a,
       // объявленной в начале функции
   }
   a = 9; // переменная a в начале функции
}


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

Можно ещё рассказать про статические глобальные переменные и статические переменные внутри функций, если знаете -- на лекциях этого не было и спрашивать мы этого не будем.
fenster
Проблема представления произвольного действительного числа в ЭВМ. Модель действительных чисел конечной точности: интервалы (классы чисел, сравнимых с заданной точностью). Арифметика интервалов. Понятие погрешности (потери точности).

Про это, Женя, рассказывать не буду, потому что было и на лекциях очень подробно, и хорошо описано в методичке. Если есть конкретные вопросы по каким-либо способам представления -- welcome.
Люциф
2fenster: прошу прощения, ноя перепутал, мне нужно было узнать ответ на вопрос

8. Проблема представления произвольного действительного числа в ЭВМ. Модель действительных чисел конечной точности: интервалы (классы чисел, сравнимых с заданной точностью). Арифметика интервалов. Понятие погрешности (потери точности).
Люциф
все дело в том, что я не могу уловить суть и найти нужное место в методичке. В Вирте тоже нет((
worklez
QUOTE (fenster @ Jun 13 2007, 02:14)
        ::a = 8; // присваивание глобальной переменной

:: -- расширение из C++.
fenster
QUOTE (worklez @ Jun 13 2007, 16:18)
QUOTE (fenster @ Jun 13 2007, 02:14)
        ::a = 8; // присваивание глобальной переменной

:: -- расширение из C++.

Это даже не расширение, а просто чистый плюсизм -- пространства имён. Своим (в 603б) я ничего такого на лекциях не рассказывал (читал чистый C), но есть подозрение, что в некоторых группах читают помесь C и C++, потому и заикнулся про это в посте. По крайней мере, на экзамене дети из других групп сплошь и рядом читают из cin и пишут в cout.

Замечу, что комментарии в стиле // -- это тоже фича из C++, которую в C (С99) добавили заметно позже smile.gif
worklez
QUOTE (fenster @ Jun 13 2007, 15:43)
Это даже не расширение, а просто чистый плюсизм -- пространства имён.

Именно. :)
QUOTE
По крайней мере, на экзамене дети из других групп сплошь и рядом читают из cin и пишут в cout.

Есть подозрение, что народ приходит уже в таком состоянии. Некоторые просто отказываются понимать/использовать "эти неудобные функции из C" :).
QUOTE
Замечу, что комментарии в стиле // -- это тоже фича из C++, которую в C (С99) добавили заметно позже :)

Но они таки есть в стандарте C :).
fenster
Проблема представления произвольного действительного числа в ЭВМ. Модель действительных чисел конечной точности: интервалы (классы чисел, сравнимых с заданной точностью). Арифметика интервалов. Понятие погрешности (потери точности).

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

То, что нужно знать всем

Ясно, что действительных чисел бесконечно много. Тем не менее, нужно научиться впихивать их в конечное число бит (скажем, для float 4 байта, т.е. 32 бита). Если при представлении целых чисел логично было жертвовать очень большими по модулю числами (мол, всё равно такие никому не нужны), то при представлении дробных чисел логично жертвовать точностью. Близкие друг к другу вещественные числа в таком случае будут считаться равными: следующая программа
CODE
#include <stdio.h>
int main()
{
       float f1 = 0.1;
       float f2 = 0.100000001;
       if (f1 == f2)
               printf("равны\n");
       else
               printf("не равны\n");
       return (0);
}

выдаст "равны" (а если убрать один нолик из f2, то будет уже "не равны"). Получается, что одному набору бит соответствует несколько чисел (целый интервал на прямой), и все действительные числа разбиваются таким образом на классы (классов этих для четырёхбайтного типа float всего 2^32, а на деле ещё меньше -- подробнее об этом ниже). Причём, что интересно, чем больше по модулю число, тем большей длины интервал "равных" ему чисел на прямой.

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

В модели с плавающей запятой ненулевые числа представляются по частям: отдельно знак s, порядок e и мантисса f:

x = s * f * 2^e

(2 -- основание системы счисления). Как это работает, надеюсь, все представляют; отмечу лишь, что мантисса f лежит в интервале [1, 2). Подробнее, опять же, ниже.

Как выполнять арифметические операции с такими числами? Для сложения и вычитания двух чисел, заданных в виде s * f * 2^e, нужно сначала привести их к одному порядку (к большему), затем сложить (вычесть) мантиссы и результат нормализовать. Для умножения и деления нужно сложить (вычесть) порядки и перемножить мантиссы, результат нормализовать.

Какие бывают погрешности и откуда берётся потеря точности? Нас интересуют два момента: нехватка знаков в мантиссе и потеря точности при приведении к большему порядку.

1. Нехватка знаков в мантиссе.

Простейший пример: число 0.1. Если попытаться перевести его в двоичную систему, получим 0.000110011001100..., т.е. 0.0(0011) -- бесконечную дробь. Получается, что на самом деле число 0.1 не может быть точно представлено в типе float. Из этого, например, следует тот факт, что цикл
CODE
float a = 0;
while (a != 1)
   a += 0.1;

зависнет, т.к. точного равенства с единицей никогда не получится.

2. Потеря точности при приведении к большему порядку.

Простейший пример: сложим 1000000 и 0.0000001.
CODE
float f1 = 1000000;
float f2 = 0.0000001;
float f = f1 + f2;
printf("%.10f\n", f1);
printf("%.10f\n", f2);
printf("%.10f\n", f);

Получим следующее:
CODE
1000000.0000000000
0.0000001000
1000000.0000000000

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

На этом "то, что нужно знать всем", заканчивается, в следующем посте напишу про то, как всё это на самом деле работает. На лекции в 603б я про это читал, но, кажется, мало кто меня до конца понял smile.gif
fenster
То, что на экзамене можно не знать, но полезно понимать "для общего развития"

Согласно стандарту IEEE 754, тип float устроен следующим образом:

знак -- 1 бит
порядок -- 8 бит
мантисса -- 23 бита

что эквивалентно такой структуре на C:
CODE
struct Float {
       unsigned int fraction: 23;
       unsigned int exponent: 8;
       unsigned int sign: 1;
};

(числа после двоеточий означают размер поля структуры в битах; так можно делать, хоть на лекции я про такое и не рассказывал smile.gif В С вообще есть ещё много того, о чём вам на уроках не говорилось...).

Реально вместо порядка e хранится число E = e + 127. Сделано это для того, чтобы можно было хранить порядок в виде беззнакового целого числа -- так их удобнее сравнивать. Кроме того, в мантиссе не хранят первую цифру (которая в нормализованном ненулевом двоичном числе всё равно всегда равна единице).

Соответственно, есть следующие варианты:

1. Если порядок числа (e) находится между -126 и 127 включительно (т.е. реально хранится E, лежащее в отрезке от 1 до 254), то число записывается в нормализованном виде "без сюрпризов". Первая цифра мантиссы не хранится. Всего существует 2 * 254 * 2^23 различных чисел в таком формате.

2. Если хранящийся порядок числа E = 0 (что как бы по идее должно соответствовать порядку e = -127), происходит несколько другое. Число считается ненормализованным с порядком -126, мантисса хранится полностью (потому что в ненормализованном числе первая цифра может быть любой). В частности, в записи числа 0 все биты равны нулю (а есть ещё -0, в котором нулю равны все биты, кроме знакового). Различных чисел из второго пункта существует 2 * 2^23.

3. Если хранящийся порядок числа E = 255 (что как бы по идее должно соответстовать порядку e = 128), а мантисса состоит из нулей, то число считается плюс или минус бесконечностью (в зависимости от знака). Бесконечностей, таким образом, две.

4. Если хранящийся порядок числа E = 255 (что как бы по идее должно соответстовать порядку e = 128), а мантисса содержит хотя бы один ненулевой бит, то считается, что данная запись некорректна -- т.е. не соответствует никакому числу. Такие "не числа" так и называются: NaN, Not a Number. Их общее количество равно 2 * (2 ^ 23 - 1).

Таким образом, всего существует 2 * 254 * 2^23 + 2 * 2^23 = 4278190080 различных чисел типа float, две бесконечности и 16777214 NaN'ов. Суммарное число вариантов равно 2^32, т.е. никого не забыли.

В аттаче к данному посту -- код на C, который показывает двоичное строение числа типа float. Пример работы ниже. Заметьте, что первый бит мантиссы (единица) не хранится для ненулевых нормализованных чисел!

CODE
Введите число: 0
 0.000000:     знак: 0, мантисса: 00000000000000000000000, порядок: 00000000 (ненормализованное число)
Введите число: 1
 1.000000:     знак: 0, мантисса: 00000000000000000000000, порядок: 01111111 (степень: 127 - 127 = 0)
Введите число: 2
 2.000000:     знак: 0, мантисса: 00000000000000000000000, порядок: 10000000 (степень: 128 - 127 = 1)
Введите число: -1
-1.000000:     знак: 1, мантисса: 00000000000000000000000, порядок: 01111111 (степень: 127 - 127 = 0)
Введите число: 0.5
 0.500000:     знак: 0, мантисса: 00000000000000000000000, порядок: 01111110 (степень: 126 - 127 = -1)
Введите число: 0.25
 0.250000:     знак: 0, мантисса: 00000000000000000000000, порядок: 01111101 (степень: 125 - 127 = -2)
Введите число: 0.75
 0.750000:     знак: 0, мантисса: 10000000000000000000000, порядок: 01111110 (степень: 126 - 127 = -1)
Введите число: 0.1
 0.100000:     знак: 0, мантисса: 10011001100110011001101, порядок: 01111011 (степень: 123 - 127 = -4)
Введите число: 0.100000001
 0.100000:     знак: 0, мантисса: 10011001100110011001101, порядок: 01111011 (степень: 123 - 127 = -4)
Введите число: 0.10000001
 0.100000:     знак: 0, мантисса: 10011001100110011001110, порядок: 01111011 (степень: 123 - 127 = -4)


Дополнительная информация: статья на Википедии про IEEE 754.
fenster
Приглашаю всех на консультацию сегодня в 10:00 (через 7 часов с небольшим) smile.gif
fenster
Все пересдачи зачётов происходят после окончания экзаменационной сессии. Конкретные даты назначает учебный отдел.
Люциф
Александр Генадьевич, скажите, может ли студент быть допущен до экзамена, имея 11,5 баллов за тест? shy.gif
fenster
Я бы не допустил, а вообще зависит от лектора.
fenster
Ну что, пришёл вот с экзамена. Поставил пятёрку, две четвёрки, две тройки и три двойки. Такая вот невесёлая статистика.

Ребята, которые будут сдавать 19го. Просьба у меня к вам. Разбирайте то, что учите! Нет смысла зубрить текст, который вы не понимаете! Разбирайтесь!

Если есть вопросы -- welcome в эту тему.
SupremEvil
Александр Геннадьевич!!! Расскажите, пожалуйста, про такой термин, как "нетривиальное программирование", только не дословно, а специфику данного способа программирования!!! Спасибо!
fenster
Не знаю такого общепринятого термина. Контекст можно?

Если имеется в виду так называемое obfuscated programming, написание как можно более нечитаемых программ, решающих некоторую задачу -- это не ко мне smile.gif
Люциф
Гы, а ведь я переписал на 14(15) и сдал на четвёрку!:р
Paradoxx [HomeNet]
Ув. Фенстер, а там такой тупой вопрос есть.. Про появление систем счисления smile.gif Что-то вроде истории появления систем счисления.. Точно не помню формулировку..
fenster
Никто не будет такое спрашивать. Если интересно "для себя", то вот первая ссылка в гугле по запросу "история систем счисления": http://www.goldenmuseum.com/1104HistoryNS_rus.html

Нам на экзамене намного интереснее понять, понимает ли студент, что это такое вообще. А то на прошлом экзамене рисовал палочки, а потом по семь палочек объединял в звёздочку, чтобы объяснить 7-ю систему %)
Paradoxx [HomeNet]
Поздравляю всех с окончанием сессии (не для всех она, правда, закончилась), желаю хороших успехов в след. году %)

П.с. и fenster'у спасибо за хорошую оценку на экзаменеsmile.gif
UNIX
много студентов не сдало экзамен ?
NoLp
10.Модель целых чисел со знаком.Знаковые разряд.Формулы для минимальных и максимальных чисел.

Щас я расскажу как я понял,есть число +1101 он в разрядной системе К,самый последний 012345 и так далее,самвй левый т.е страший,показыввет знак числа если +,то 1,если -,то 0 ,а вот дальше не понимаю почему из,+1101 составилась таблица
001101 с К=6,как можно задавать К? если число и так состоит из К разрядов,а их тут 5(если знак числа учитывать как разряд не могу понять)
fenster
Подняли-таки старую тему smile.gif Не, неправильно поняли. Чтобы понять, как строятся целые числа со знаком, надо рассмотреть такую табличку (говорим сейчас о восьмибитовом типе, например, char):

Код
число двоичный вид
+000  00000000
+001  00000001
+002  00000010
+003  00000011
+004  00000100
...
+126  01111110
+127  01111111
-128  10000000
-127  10000001
...
-002  11111110
-001  11111111
+000  00000000


Идея следующая. Нам надо сделать так, чтобы в k битах (в примере k = 8) можно было представить одинаковое количество положительных и отрицательных чисел. Совсем одинаковое не получится из-за нуля, поэтому договариваются так: все k-разрядные двоичные записи, начинающиеся с нуля, считают представлением неотрицательных чисел, а начинающиеся с единицы -- отрицательных. Кодирование отрицательных чисел нужно придумать так, чтобы N в сумме с -N давало 0. Поэтому при построении кода для -N берётся код для N, в нём нули заменяются единицами (и наоборот). Если сложить такой код с кодом N, получим все единицы, для полного счастья добавляем ещё одну единицу и (за счёт отсечения "лишнего" разряда) получим все нули. То есть, если N = 6 = 00000110, то -N = 11111010 (сначала из 00000110 получили 11111001, потом к результату добавили 1).

Это вот надо понять smile.gif Тогда и формулы для максимального и минимального числа станут совсем очевидными.
linus
Цитата(fenster @ 24.07.2008, 0:55) *
....


Как жалко, что вы не преподаете больше в ВКИ, хорошо объясняете.
p.s А в этом году "олимпиадные задачи" будете вести? Надеюсь я не один буду smile.gif А то помню пришел, все второкурсники, а я один первокурсник там.

Извиняюсь за offtop...
Формулы да и вправду очевидными становятся ;)
PEKETuP
Цитата(fenster @ 23.07.2008, 23:55) *
Это вот надо понять smile.gif Тогда и формулы для максимального и минимального числа станут совсем очевидными.


допустим хачу сложить 1 и 1:
01
and
01
=
shl 01 = p1:10

01
xor
01
= 00

00 ----- пусть это х1
xor
p1:10 ------ а это х2
=
10 - это ответ т.к. еслив сделать x1 and x2 получим 0

действительно 1 + 1 = 2 (01+01=10 в двоичной)

{
11111110
and
00000010
=
shl 00000010 = p[i]:00000100

11111110
xor
00000010
=
11111100

11111100
xor
p1:00000100
=
11111000

}
while (p[i]!=0) => в конце цикла получаем переполнение т.е. 100000000 и это есть якобы 0.


А вот про формулы макс. и мин. я чета прослушал =)поясните(:

З.Ы. это мне объясняли далеко не на методах =)
З.ы.ы. в топку формулы даеш понимание
11
01

10
10

100
000

100
fenster
Если я правильно понял, то у вас тут причина со следствием перепутана. Не двоичная система так устроена из-за процессорных команд, а совсем наоборот smile.gif

Сами формулы без понимания идеи, ясно дело, никому не нужны. А если идея понятна, то формулы за секунду пишутся (если, опять же, понимать, что количество различных слов длины n из нуля и единицы -- два в степени n, что должно быть очевидно любому десятикласснику).
PEKETuP
раздел не про булеву алгебру =)а про методы ... мне в 10 классе ни приснилось бы даже в страшном сне такое как количество перестановок с повторениями двух элементов по Н позициям равно два в степени Н =)

и как мне написать формулы ? непонятно....
fenster
А это потому что вы всё сложными словами называете. А если сказать, что для одного разряда два варианта, для двух получается четыре (каждый вариант первого разряда на каждый вариант второго), для трёх аналогично восемь, то только самый-самый тормоз не догадается, что там 2^n. За пять лет в ВКИ проверено smile.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Русская версия IP.Board © 2001-2024 IPS, Inc.