Научная статья на тему 'РАССТАНОВКА ЛАДЕЙ НА ШАХМАТНОЙ ДОСКЕ'

РАССТАНОВКА ЛАДЕЙ НА ШАХМАТНОЙ ДОСКЕ Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
62
7
i Надоели баннеры? Вы всегда можете отключить рекламу.
i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «РАССТАНОВКА ЛАДЕЙ НА ШАХМАТНОЙ ДОСКЕ»

Муромцев В. В., к.т.н.

доцент Атрахименок Я.М.

студент

направление подготовки «Прикладная информатика»

Белгородский государственный национальный исследовательский университет

Россия, г. Белгород РАССТАНОВКА ЛАДЕЙ НА ШАХМАТНОЙ ДОСКЕ

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

В узком смысле под программированием понимается написание инструкций - программ на конкретном языке программирования (часто по уже имеющемуся алгоритму - плану, методу решения поставленной задачи) [1].

Задачей данной научной работы является расстановка ладей на шахматной доске. Она сводится к алгоритму перестановок. Цель работы -изучить литературу по данной теме, определить, в чем суть алгоритма перестановок, на основе полученных знаний разработать программы и провести сравнительный анализ, который выявит, какая из программ является более эффективной.

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

В данной работе ввиду эффективности в качестве языка программирования был выбран язык СИ, так как это современный язык, включающий в себя те управляющие конструкции, которые рекомендуются теоретическим и практическим программированием.

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

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

В данном проекте рассмотрены следующие аспекты:

1. Разработка алгоритмов и структур данных;

2. Программная реализация алгоритмов;

3. Сравнительный анализ алгоритмов.

Проведём сведение поставленной задачи к основным комбинаторным алгоритмам.

Целью данной работы является разработка программы для решения задачи, заключающейся в том, чтобы расставить n ладей на доске размером n*n, так чтобы они не атаковали друг друга. Для лучшего изучения данной задачи следует рассмотреть несколько примеров. Одним из них является доска 3*3. Для обозначения местонахождения ладей был выбран знак Х.

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

Рисунок 1 - Пример шахматной доски, при n=3.

Придерживаясь правила об отсутствии атак ладей, было выявлено 6 верных вариантов: 123, 132, 213,231,312,321(Рис. 1).

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

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

Перестановкой множества из n элементов называется расположение элементов в определенном порядке [3]. Так, все различные перестановки множества из трех элементов {1, 2, 3} - это

(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)

Число всех перестановок из n элементов обозначается Pn (от начальной буквы французского слова "permutation", что значит "перестановка", "перемещение"). Для любого множества из n элементов существует ровно n!=1 *2*.. ,*n различных перестановок. Это легко показать. Пусть P(n) - число перестановок n-элементного множества. Рассмотрим для определённости множество M= {1,2.,n}. На первом месте в перестановке может стоять одно из чисел 1,..,n. Перестановок множества М, в которых на первом месте стоит число i ровно столько, сколько есть различных перестановок оставшихся n-1 чисел, то P(n-1). Потому, P(n) = n* P(n-1).

По индукции получаем P(n) = 1*2*.. ,*n = n!

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

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

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

Рассмотрим реализацию алгоритма порождения перестановок нерекурсивным способом.

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

клетки шахматной доски, то есть оставшиеся элементы матрицы закрасили другим символом '0'. Сами перестановки будут осуществляться в массиве a[i] в лексикографическом алгоритме или в массиве X[i] в рекурсивном алгоритме.

Рассмотрим нерекурсивный метод генерации перестановок. Мы построим нерекурсивную функцию Next, которая по данной перестановке будет строить следующую за ней в лексикографическом порядке. Для этого зададимся вопросом: в каком случае можно увеличивать k-й член перестановки, не меняя предыдущих? Ответ: если он меньше какого-нибудь из следующих членов (с номерами больше k). Отсюда сразу получается алгоритм для Next:

1) Ищем наибольшее k такое, что a[k] < a[k + 1] > ... > a[n - 1] .

2) Значение a[k] нужно увеличить минимально возможным образом. Для этого среди a[k+ 1], . . . , a[n - 1] находим минимальный элемент а[1],больший а[к]и меняем его с a[k].

3) Осталось только расположить числа с номерами большими k в возрастающем порядке. И это легко сделать, так как они уже расположены в убывающем.

Теперь чтобы вывести перестановки мы, можем начать с (1, 2, . . . , n) и вызывать Print_perm до тех пор пока не дойдем до последней перестановки (n, n - 1, . . . , 1).

int next(int *a, int N) {//содержит некоторую перестановку чисел от 1

до n.

int k, t, i, 1тр;//находим к: такой что a[k] < a[k+1] >...>a[n-1]. for (k = N - 2; k >= 0 && a[k] > a[k+1]; k-- ); if (k == -1)

return 0;//это последняя перестановка. for (t = k + 1; t < N - 1 && a[t+1] > a[k] ; t++); //находим t > k, такое что a[t] - минимальное число большее а[к] среди a[k+1]...a[n-1]

//меняем местами

tmp = a[k]; a[k] = a[t]; a[t] = tmp;

// участок массива a[k+1] ... a[n-1] записываем в обратном порядке for(i = k + 1; i <= (N + k) / 2; i++) { t = N + k - i;

tmp = a[i]; a[i] = a[t]; a[t] = tmp; }

return 1;

}

Вывод доски на экран осуществляется с помощью функции print_perm. Здесь мы использовали цикл БОЩпараметрический). Цикл имеет вид:

For (выражение! выражение-условие; выражение_3) Тело цикла

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

Также использовалась функция printf(). Она переводит данные из внутреннего кода в символьное представление и выводит полученные изображения символов результатов на экран дисплея.

Блок-схемы и программу с решенной задачей, где порождения перестановок осуществляются итеративным способом, выглядят следующим образом:

#include <stdio.h> #include <stdlib.h> #include <malloc.h> int a[100]; char M[100][100]; int N;

void print_perm(int *a, int N) { int i;

for (i=0; i<N; i++){ int j;

for (j=0; j<N; j++) { M[i][j]='0';

}

}

for (i=0; i<N; i++){ M[i][a[i]-1]='X';

}

for (i=0; i<N; i++){ int j;

for (j=0; j<N; j++) { printf("%c ",M[i][j]);

}

printf("\n"); }

printf("\n");

}

int next(int *a, int N) {//содерит некоторую перестановку чисел от 1 до

n.

int k, t, i, 1тр;//находим к: такой что a[k] < a[k+1] >...>a[n-1]. for (k = N - 2; k >= 0 && a[k] > a[k+1]; k-- ); if (k == -1)

return 0;//это последняя перестановка. for (t = k + 1; t < N - 1 && a[t+1] > a[k] ; t++);

//находим t > k, такое что a[t] - минимальное число большее a[k] среди a[k+1]...a[n-1]

//меняем местами

tmp = a[k]; a[k] = a[t]; a[t] = tmp;

// участок массива a[k+1] ... a[n-1] записываем в обратном порядке for(i = k + 1; i <= (N + k) / 2; i++) { t = N + k - i;

tmp = a[i]; a[i] = a[t]; a[t] = tmp; }

return 1;

}

int main() {

int *a, N, i; scanf( "%d", &N ); printf("N=%d\n\n", N); a = (int*) malloc( N * sizeof(int) ); for( i = 0; i < N; i++ ) a[i] = i + 1; do {

print_perm( a, N ); } while ( next(a, N) );

return 0;

}

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

Блок-схема программы: Функция Generate():

X[¡]=¡ + 1

Функция Printf_perm():

Функция Swap():

Рассмотрим реализацию рекурсивного способа порождения перестановок.

Будем перебирать перестановки чисел {1, 2, ... , n} в глобальном массиве Х: (Х[0], ... , Х[п - 1]), последовательно заполняя его числами 1, ..., n в различном порядке.

Для перебора всех перестановок можно применить ту же рекурсивную идею, что и при доказательстве формулы числа перестановок (см. выше). А именно, мы можем записывать на первое место в массиве Х числа 1, . , n по очереди, и для каждого числа рекурсивно вызывать функцию генерации перестановок оставшихся n - 1 чисел.

За генерацию перестановок отвечает функция Generate.

void Generate(int к){//генерация перестановок if (k==N) {

print_perm ();

}

else { int j;

for (j=k; j<N; j++){ Swap(k,j);

Generate(k+1);k,// рекурсивно вызываем функцию генерации перестановок оставшихся чисел

Swap(k,j); }

}

}

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

#include <stdio.h> #include <stdlib.h> int X[100]; char M[100][100]; int N;

void Swap(int a,int Ь){//обеспечивает перестановку элементов int t=X[a]; X[a]=X[b]; X[b]=t;

}

void print_perm (){//выводит на экран int i;

for (i=0; i<N; i++){ int j;

for (j=0; j<N; j++) {

M[i][j]='0';//заполнили всю матрицу символом '0'

}

}

for (i=0; i<N; i++){

M^X^-^-Xy/окрасили местоположения ладей символом 'X'

}

for (i=0; i<N; i++){ int j;

for (j=0; j<N; j++) {

printf("%c м,M[i][j]);//вывод на экран полученной матрицы

}

printf("\n"); }

printf("\n"); }

void Generate(int k){//генерация перестановок if (k==N) { print_perm ();

}

else { int j;

for (j=k; j<N; j++){ Swap(k,j);

Generate(k+1);k,// рекурсивно вызываем функцию генерации перестаноок оставшихся чисел Swap(k,j);

}

}

}

int main(){

printf("Варианты перестановок при "); scanf("%d",&N);

printf("N=%d\n\n" ,N); int i;

for (i=0; i<N; i++) {//массив, в котором будем осуществлять перестановки, начиная с 1.

X[i]=i+1;

}

Generate(0);

Блок-схема программы: Функция №х1;():

Проведём сравнительный анализ реализованных алгоритмов.

Для начала протестируем программы. Пусть это будет при N=2 и N=3 (входные данные).

При N=2 , количество перестановок Р = 2! = 2, а при N=3, количество перестановок Р = 3! = 6 (выходные данные).

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

Копии экрана программы, реализованные рекурсивным способом:

Копия экрана работы программы, реализованная итеративным способом:

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

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

Существует множество критериев сравнения, но будем использовать, на мой взгляд, два самых важных:

1) по занимаемому объему памяти;

2) по быстродействию.

Так как рекурсивный метод использует внутреннюю память, следовательно, занимает больший объем, то по первому критерию оптимальным алгоритмом является лексикографический.

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

Системное время мы будем вызывать с помощью функции time_t time (time_t *time). Функция time() возвращает текущее календарное время системы. Если в системе отсчет времени не производится, возвращается значение -1.

Функцию time() можно вызывать либо с нулевым указателем, либо с указателем на переменную типа time_t. В последнем случае этой переменной будет присвоено календарное время. Пример

Эта программа отображает местное время, определенное системой: #include <time.h> #include <stdio.h> int main(void){ struct tm *ptr; time_t lt; lt = time(NULL); ptr = localtime(&lt); printf(asctime(ptr)); return 0;

}

Функция asctime возвращает указатель на строку, которая содержит информацию, сохраняемую в адресуемой параметром ptr структуре и имеющую следующую форму:

День_недели месяц дата часы:минуты:секунды год\п\0 . struct tm *localtime(const time_t *time);

Функция localtime() возвращает указатель на структуру типа tm, содержащую время в разделенной на компоненты форме. Время представлено как местное. Указатель time обычно получают с помощью функции time().

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

Копии работы программ можно увидеть ниже.

Лексикографический способ:

Рекурсия:

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

Для данного графика мы будем использовать данные из Таблицы.

Таблица 1 - Данные для графика.

0 10 11 12 13

Лексикографический метод, 1 в сек 0 2 4 50 623

Рекурсивный метод, 1 в сек 0 2 6 71 937

По оси ОХ мы будем откладывать число элементов в нашей перестановке, а по оси ОУ - время работы программы в секундах.

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

Подведём итоги данной научной работы.

Целью была разработка и реализация на языке высокого уровня алгоритма решения задачи на тему: «Расстановка ладей на шахматной доске».

Ввиду высокой эффективности языком реализации программного продукта был выбран высокоуровневый язык Си.

В работе были достигнуты следующие цели:

1) Поставленная задача была сведена к типовому комбинаторному алгоритму;

2) Изучены различные способы порождения перестановок;

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

Использованные источники:

1. Ворожков А.В., Винокуров Н.А. Алгоритмы: построение, анализ и реализация на языке программирования Си. М., 2007. С. 205-220.

2. Подбельский В.В., Фомин С.С. Программирование на языке Си: Учеб. Пособие. - М.: Финансы и статистика, 2005.-600с.

3. Дискретная математика и комбинаторика.: Пер. с англ. - М. : Издательский дом «Вильямс», 2004. - 960с. : ил. - Парал. тит. англ.

Нахапетян Г.А. студент

Институт Информационных Технологий и Телекоммуникаций Северо-Кавказский Федеральный Университет

Россия, г. Ставрополь КРИПТОВАЮТЫ И ИХ ПРАВОВОЙ СТАТУС НА ПРИМЕРЕ

BITCOIN

В наше время электронная валюта давно перестала быть чем то особенным. Большинство пользователей ПК хотя бы раз в жизни пользовались услугами таких сервисов как QIWI, Уапёех.Деньги, расплачивались при помощи кредитной или дебетовой карты через интернет. Все эти системы схожи по своему принципу работы: они подчинены компании-владельцу, которая так же является посредником для всех проводимых операций. Данный факт обуславливает множество недостатков для потребителя, например, из-за централизованности системы снижается скорость выполнения транзакций, а стабильность работы системы полностью зависит от надёжности серверов компании-владельца.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

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

Bitcoin, Биткоин (англ. bit — единица информации «бит», англ. coin — "монета") - Электронная валюта, основанная на ПО с открытым исходным кодом с использованием пиринговых сетей и асимметричного шифрования. Минимальная денежная единица в системе названа в честь предполагаемого основателя системы Сатоши Накамото. 1 Сатоши равен 0.00000001 BTC Биткоина

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

i Надоели баннеры? Вы всегда можете отключить рекламу.