Professional Documents
Culture Documents
та природокористування
04-04-200
МЕТОДИЧНІ ВКАЗІВКИ
Частина І
Рекомендовано
методичною комісією
напряму підготовки
"Комп’ютерна інженерія"
Протокол № 7
від 03 березня 2017 р.
Рівне 2017
Методичні вказівки для виконання лабораторних робіт з дисципліни
"Програмування під платформу .NET" студентами напряму підготовки
6.050102 "Комп’ютерна інженерія". Частина І. / П. В. Ольшанський, –
Рівне: НУВГП, 2017, – 32 с.
2
Лабораторна робота №1.
Програмування лінійних алгоритмів мовою C# .NET
Приклад 1.1. Обмін значень. Обміняти місцями в пам’яті
комп’ютера значення двох змінних a та b однакового типу
(наприклад, int).
Розв’язок. Потрібно використати ще одну змінну t того самого типу
для проміжного зберігання початкового значення змінної a :
register int t = a ; a = b ; b = t ;
Завдання 1.1. Обміняти місцями в пам’яті комп’ютера значення
трьох змінних a, b, c типу int. Розглянути всі можливі варіанти
обміну.
Приклад 1.2. Розв’язати задачу з прикладу 1.1 без використання
третьої проміжної змінної.
Розв’язок.
Варіант 1. З використанням арифметичних операцій:
a=a+b; b=a-b; a =a-b;
Або в скороченій формі:
a+=b; b=a-b; a-= b;
або
a = - (a - = b) + (b + = a);
або
a = - (a - = b + = a); b - = a ;
Пояснення
Нехай a = a0; b = b0.
Тоді a = a0 + b0; b = (a0 + b0) - b0 = a0; a = (a0 + b0) - a0 = b0 .
Трюк полягає в тому , що для виконання арифметичних операцій
використовується щонайменше два регістри процесора, в той час, як
для пересилання даних між змінними - досить одного, тому хоч
змінної t в записі програми немає, її роль виконує один з регістрів
процесора.
Варіант 2. З використанням логічної операції XOR (виключне АБО):
a=a^b; b=b^a; a =a^b;
або в скороченій формі:
a^=b; b ^=a; a^= b;
або b^=a ^=b; a^= b;
3
Якщо в першому варіанті можливе переповнення або втрата
значень при додаванні чисел різного порядку, то другий варіант
коректно виконується з даними будь-якого типу, який допускає
побітові логічні операції.
Завдання 1.2. Обміняти місцями в пам’яті комп’ютера значення двох
змінних a, b, c типу float (double) з використанням операцій множення
і ділення.
Завдання 1.3*. Обміняти місцями в пам’яті комп’ютера значення
двох змінних a, b, c типу int з використанням операцій множення,
цілочисельного ділення та алгебраїчного модуля (%).
Приклад 1. 3. Використовуючи тільки оператор присвоєння і
операцію множення, обчислити a8, a16, a32, a64 за найменшу кількість
операцій.
Розв’язок.
b=a*a; b = b * b ; /// a4 b = b * b ; /// a8
16 32
b = b * b ; /// a b = b * b ; /// a b = b * b ; /// a64
Приклад 1. 4. Використовуючи тільки оператор присвоєння і
операцію множення обчислити a9, a27, a45, a100 за найменшу кількість
операцій множення.
Розв’язок для а в 9-му степені.
Розв’язок для а в 27-му
a2 = a * a ;
степені
a4 = a2 * a2 ; /// a4
a8 = a4 * a4 ; /// a8 a3 = a * a * a ;
a9 = a8 * a ; /// 4 операції * 4 = a27 = a3 * a3 * a3 ;
або інший варіант /// 4 операції * 2 =
a3 = a * a * a ;
a9 = a3 * a3 * a3 ; /// 4 операції * 2 =
Розв’язок для а в 45-му степені aбо інший варіант
(45= 32+8+4+1 ) (45=15+15+15)
a2 = a * a ; a2 = a * a ;
a4 = a2 * a2 ; /// a4 a4 = a2 * a2 ; /// a4
a8 = a4 * a4 ; /// a8 a8 = a4 * a4 ; /// a8
a16 = a8 * a8 ; /// a16 a15 = a8 * a4 * а2 * a ; /// a15
a32 = a16 * a16 ; /// a32 a45 = a15 * a15 * a15 ;
a45 = a32 * a8 * a4 * a ;
/// 8 операцій * 5 =
/// 8 операцій * 6 =
4
Розв’язок для а в 100-му степені (100= 64+32+4 )
a2 = a * a ;
a4 = a2 * a2 ; /// a4
a8 = a4 * a4 ; /// a8
a16 = a8 * a8 ; /// a16
a32 = a16 * a16 ; /// a32
a64 = a32 * a32 ; /// a64
a100 = a64 * a32 * a4 ; /// 8 операцій *
Завдання 1.4. Використовуючи тільки оператор присвоєння і
операцію множення, обчислити a11, a22, a44, a99 за найменшу кількість
операцій.
Приклад 1. 4. Скласти програму для обчислення площі трикутника
за відомими довжинами сторін a, b, c.
Розв’язок
using System;
namespace Heron
{ class Program
{ public static void Main(string[] args)
{Console.WriteLine(
"Обчислення площі трикутника за формулою Герона");
double a,b,c;
Console.Write("Введiть значення a = ");
a = double.Parse(Console.ReadLine());
Console.Write("Введiть значення b = ");
b = double.Parse(Console.ReadLine());
Console.Write("Введiть значення c = ");
c = double.Parse(Console.ReadLine());
double p = ( a + b + c ) / 2 ;
double S = Math.Sqrt( p*(p - a)*(p - b)*(p - c));
Console.WriteLine("Площа трикутника S = {0}", S);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
5
Приклад 1. 5. Скласти програму для обчислення площі трикутника
за відомими координатами вершин A( x1, y1 ), B( x2, y2 ), C( x3, y3 ).
Розв’язок
using System;
namespace TrianglesArea
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine( "Обчислення площі трикутника
за координатами вершин");
double x1,y1,x2,y2,x3,y3;
Console.Write("Введiть значення x1 = ");
x1 = double.Parse(Console.ReadLine());
Console.Write("Введiть значення y1 = ");
y1 = double.Parse(Console.ReadLine());
Console.Write("Введiть значення x2 = ");
x2 = double.Parse(Console.ReadLine());
Console.Write("Введiть значення y2 = ");
y2 = double.Parse(Console.ReadLine());
Console.Write("Введiть значення x3 = ");
x3 = double.Parse(Console.ReadLine());
Console.Write("Введiть значення y3 = ");
y3 = double.Parse(Console.ReadLine());
double S = Math.Abs((x1 *(y2 - y3) +
x2 *(y3 - y1) + x3 *(y1 - y2))/2);
Console.WriteLine("Площа трикутника S = {0}", S);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
6
Використання нестандартних функцій
Приклад 1. 6. Скласти програму для обчислення площі опуклого
чотирикутника за відомими довжинами сторін a, b, c, d та діагоналі f .
Використати функцію для обчислення площі трикутника
Розв’язок
using System;
namespace QuadsArea
{ class Program
{public static void Main(string[] args)
{
Console.WriteLine(
"Обчислення площі чотирикутника");
double a,b,c,d,f;
Console.Write("Введiть значення a = ");
a = double.Parse(Console.ReadLine());
Console.Write("Введiть значення b = ");
b = double.Parse(Console.ReadLine());
Console.Write("Введiть значення c = ");
c = double.Parse(Console.ReadLine());
Console.Write("Введiть значення d = ");
d = double.Parse(Console.ReadLine());
Console.Write("Введiть значення f = ");
f = double.Parse(Console.ReadLine());
double S = TrianglesArea( a,b,f) +
TrianglesArea( c,d,f);
Console.WriteLine(
"Площа чотирикутника S = {0}", S);
}
static double TrianglesArea(double a, double b,
double c)
{
double p = ( a + b + c ) / 2 ;
double S = Math.Sqrt(
p * ( p - a ) * ( p - b ) * ( p - c ) ) ;
return S ;
}
}
}
7
Лабораторна робота №2.
Програмування розгалужених алгоритмів
мовою C# .NET
Приклад 2.1. Скласти програму для визначення найменшого із
чотирьох заданих чисел a, b, c, d . Використати функцію для
визначення меншого із двох заданих чисел.
Розв’язок
using System;
namespace MinABCD
{
class Program
{
public static void Main(string[] args)
{ Console.WriteLine("Визначення найменшого
із чотирьох заданих чисел ");
double a,b,c,d;
Console.Write("Введiть значення a = ");
a = double.Parse(Console.ReadLine());
Console.Write("Введiть значення b = ");
b = double.Parse(Console.ReadLine());
Console.Write("Введiть значення c = ");
c = double.Parse(Console.ReadLine());
Console.Write("Введiть значення d = ");
d = double.Parse(Console.ReadLine());
double min = MinXY( MinXY(a,b),MinXY(c,d));
Console.WriteLine(
"Найменше із {0},{1},{2},{3} = {4}",a,b,c,d,min);
}
static double MinXY(double x, double y)
{double m;
if (y > x) m = x;
else m = y;
Console.WriteLine(
"Мінімум iз {0} та {1} дорiвнює {2}", x, y, m);
return m ;
}
}
}
Зауваження. Умовний оператор може записуватись всередині
формул у вигляді тернарної операції:
double m = (y > x) ? x : y;
8
Приклад 2.2. Визначити, яке з двох заданих чисел x та y менше, а яке
більше. Скласти програму без використання умовного оператора.
Розв’язок.
double min = (x + y) / 2 – Math.Abs(x - y)/2;
double max = (x + y) / 2 + Math.Abs(x - y)/2;
Зауваження. Насправді умовний оператор неявно використовується
при обчисленні функції Abs(x) :
if (x<0) Abs = -x ; else Abs = x
Приклад 2.3. Скласти програму для знаходження коренів
квадратного рівняння a*x*x+b*x+c=0
Розв’язок.
using System;
namespace SquareEquation
{class Program
{public static void Main(string[] args)
{ Console.WriteLine(
"Розв’язування квадратного рівняння");
double a,b,c,d;
Console.Write("Введiть значення a = ");
a = double.Parse(Console.ReadLine());
Console.Write("Введiть значення b = ");
b = double.Parse(Console.ReadLine());
Console.Write("Введiть значення c = ");
c = double.Parse(Console.ReadLine());
d =b*b-4*a*c;
if (d<0)
{d = Math.Sqrt(-d);
double re = -b/(2*a);
double im = d/(2*a);
Console.WriteLine( "Комплексні корені
x1 = {0}+{1}i, x2 = {0}-{1}i",re,im);
}
else
{d = Math.Sqrt(d);
double x1 = (-b+d)/(2*a);
double x2 = (-b-d)/(2*a);
Console.WriteLine(
"Дійсні корені x1 = {0}, x2 = {1}", x1, x2);
}
}
}
}
9
Завдання 2.1. Скласти програму для знаходження всіх дійсних
коренів біквадратного рівняння та обчислення їх кількості
a*x*x*x*x+b*x*x+c=0 або a*z*z+b*z+c=0 , z=x*x
Календарні розрахунки
Приклад 2. 4. Скласти програму для визначення дня тижня для
заданої дати ХХ та ХХІ століть.
Розв’язок. Враховуючи, що 31 грудня 1989 року була неділя,
знайдемо кількість днів, яка пройшла від початку двадцятого сторіччя
до заданої дати. Потім за залишком від ділення на 7 можна визначити
день тижня.
При цьому календарні розрахунки (обчислення кількості
пройдених днів) можна значно спростити, якщо початок року
перенести з 1 січня на 1 березня. Тоді кожен з пройдених 11 місяців
матиме 30 або 31 день і з допомогою множення числа 30,59 на
кількість пройдених від 1 березня місяців і виділення цілої частини
одержаного добутку дістанемо точне число днів за скоректовану
кількість місяців. Додавши цілу частину добутку 365,25 на кількість
пройдених років і число днів за останній місяць, отримаємо шукану
кількість днів.
Наведений алгоритм коректно працює для дат ХХ і ХХІ
століть. Для інших століть потрібно враховувати високосні сторіччя за
григоріанським календарем: якщо номер століття ділиться націло на 4,
то останній рік має 366 днів, якщо ні , то – 365. Наприклад, 1600, 2000
рік - 366 днів, 1700,1800,1900,2100 – 365.
using System;
namespace XX_XXI_Century
{ class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Календарні розрахунки");
int day, month, year;
Console.Write("Введiть число day = ");
day = int.Parse(Console.ReadLine());
Console.Write("Введiть місяць month = ");
month = int.Parse(Console.ReadLine());
Console.Write("Введiть рік year = ");
year = int.Parse(Console.ReadLine());
int cor = ( 12 -month ) / 10 ;
int year1 = year - cor ;
10
int month1 = month + 12 * cor - 2 ;
int number = (int)( (double)((year1-1900)*365.25))
+(int)((double)( month1 * 30.59 )) + day + 29 ;
int dayOfWeek = number % 7 ;
switch (dayOfWeek)
{
case 0:
Console.WriteLine("Sunday");
break;
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
Console.WriteLine("Saturday");
break;
}
}
}
}
Завдання 2.2. Скласти програму для визначення відстані (кількості
днів) між двома заданими датами ХХ та ХХІ століть. Обчислити
кількість прожитих Вами днів.
11
Лабораторна робота №3.
Програмування циклічних алгоритмів
мовою C# .NET
Табулювання функції
Приклад 3.1. Скласти програму для побудови таблиці значень
функції y=sin x , для x в діапазоні від x0=0 до x1=45 градусів з
кроком h=5 градусів.
Розв’язок з оператором циклу з передумовою while
while (<умова повторення>) <тіло циклу>;
using System;
namespace Tabulation
{ class Program
{public static void Main(string[] args)
{Console.WriteLine("Табулювання функції ");
double x0, x1, h;
Console.Write("Введiть початок відрізка x0 = ");
x0 = double.Parse(Console.ReadLine());
Console.Write("Введiть кінець відрізка x1 = ");
x1 = double.Parse(Console.ReadLine());
Console.Write("Введiть крок h = ");
h = double.Parse(Console.ReadLine());
double x = x0 ;
while (x <= x1)
{ double y = Math.Sin ( x * Math.PI /180 ) ;
Console.WriteLine(
" x = {0:F2} y = {1:F16} \n", x, y);
x = x + h ;
} } } }
12
Розв’язок з оператором циклу з параметром for
for(<ініціалізація параметра>; <умова повторення>;
<зміна параметра> ) <тіло циклу>;
double x = x0 ;
int n = (int) ((x1 - x0)/h) ;
for ( int i = 0 ; i <= n ; i++)
{ double y = Math.Sin ( x * Math.PI /180 ) ;
Console.WriteLine(
" x = {0:F2} y = {1:F16} \n", x, y);
x = x + h ;
}
Скінченна сума
Приклад 3.2. Cкласти програму для обчислення суми квадратів цілих
чисел від 1 до 100. S = 12+22 +32+42+52 +62+72 +82 +92 +...+1002
Розв’язок
double s = 0.0d ;
for ( int i = 1 ; i <= 100 ; i++ )
s = s + (double) (i*i) ;
Console.WriteLine(" s = {0}", s);
Сума знакозмінного ряду
Приклад 3.3. Cкласти програму для обчислення суми знакозмінного
ряду S=1 - 1/2+1/3 - 1/4+1/5 - 1/6 + ... -1/100000
а) в прямому порядку ;
б) в оберненому порядку ;
а) в прямому порядку окремо додатні та від’ємні числа;
б) в оберненому порядку окремо додатні та від’ємні числа.
Порівняти одержані результати. Пояснити виникнення похибок. Який
з варіантів є найбільш точним?
Розв’язок
double s = 0.0d ; double z = 1.0d ;
for ( int i = 1 ; i <= 100000 ; i++ )
{ s = s + z/(double)i ; z=-z ;}
Console.WriteLine(
"Сума в прямому порядку s = {0:E16}", s);
s = 0.0d ; z = -1.0d ;
for ( int i = 100000 ; i >= 1 ; i-- )
{ s = s + z/(double)i ; z=-z ;}
Console.WriteLine(
"Сума в зворотньому порядку s = {0:E16}", s);
13
double s1 = 0.0d ;
for ( int i = 1 ; i < 100000 ; i+=2 )
s1 = s1 + 1.0d /(double)i ;
Console.WriteLine(
"Сума додатніх в прямому порядку s1 = {0:E16}", s1);
double s2 = 0.0d ;
for ( int i = 2 ; i <= 100000 ; i+=2 )
s2 = s2 + 1.0d /(double)i ;
Console.WriteLine(
"Сума від’ємних в прямому порядку s2 = {0:E16}", s2);
Console.WriteLine("s1 - s2 = {0:E16}", s1-s2);
s1 = 0.0d ;
for ( int i = 99999 ; i >= 1 ; i-=2 )
s1 = s1 + 1.0d /(double)i ;
Console.WriteLine(
"Сума додатніх в зворотньому порядку s1 = {0:E16}",s1);
s2 = 0.0d ;
for ( int i = 100000 ; i > 1 ; i-=2 )
s2 = s2 + 1.0d /(double)i ;
Console.WriteLine(
"Сума від’ємних в зворотньому порядку s2={0:E16}",s2);
Console.WriteLine("s1 - s2 = {0:E16}", s1-s2);
Алгоритм Евкліда
Приклад 3.4. Скласти програму для визначення найбільшого
спільного дільника двох натуральних чисел за алгоритмом Евкліда,
який грунтується на властивості :
Найбільший спільний дільник двох чисел не змінюється,
якщо одне з чисел замінити їх різницею
Розв’язок.
int x, y;
Console.Write("Введiть число x = ");
x = int.Parse(Console.ReadLine());
Console.Write("Введiть число y = ");
y = int.Parse(Console.ReadLine());
while (x != y )
{ if (x>y) x-= y ; else y-=x; }
Console.WriteLine("НСД = {0}", x);
Числа Фібоначчі
Приклад 3.5. Задано x1=1, x2=1 - перших два елементи числової
поcлідовності. Кожен наступний елемент дорівнює сумі двох
попередніх. Скласти програму для обчислення елемента з номером n.
14
int n;
Console.Write("Введiть номер n= ");
n = int.Parse(Console.ReadLine());
int x1 = 1; int x2 = 1 ;
for (int i = 3 ; i<=n ; i++)
{
int x:= x1 + x2 ; x1 = x2 ; x2 = x ;
}
Console.WriteLine(" x[{0}] = {1}", n, x);
Cкладні проценти
Завдання 3.1. За один хід поршня з резервуару відкачується k
відсотків повітря. Обчислити залишок повітря після n ходів, якщо
початкова маса m.
_ 765 |_2_
744 372|_2_
----- 372 186|_2_
1 ----- 186 93|_2_
0 ----- 92 46|_2_
0 --- 46 23|_2_
1 --- 22 11|_2_
0 --- 10 5 |_2_
1 --- 4 2|_2_
1 -- 2 1
1 --
0
(765)10= (1011101001)2
15
int n;
Console.Write("Введiть ціле число n= ");
n = int.Parse(Console.ReadLine());
string s = "";
do
{if ( n % 2 == 1) s = "1" + s ;
else s = "0" + s;
n = n / 2 ;
}
while ( n != 0) ;
Console.WriteLine("Binary = {0}", s);
Двійкові дроби
Приклад 3.7. Cкласти програму для переведення десяткових дробів в
двійкову систему числення з точністю K розрядів.
Розв’язок. Переведення відбувається з допомогою послідовного
множення заданого десяткового дробу на 2. Якщо отримується
число>1, в двійкове представлення записується цифра “1”, інакше
цифра “0”. Поскільки скінченні десяткові дроби в двійковій формі
стають для більшості випадків нескінченними, то на певному кроці
процес множення обривають.
0, 678 double r ;
х 2 int k = 10; /// точність
--------- Console.Write(
1, 356 (-1)
х 2 "Введiть десятковий дріб
--------- (число від 0.0 до 1.0) r = ");
0, 712 ( 0) r = double.Parse(Console.ReadLine());
х 2 string s = "0.";
--------- for ( int i = 1; i<=k ; i++)
1, 424 (-1)
х 2 {
--------- r *= 2.0d;
0, 848 (0) if (r < 1.0d ) s += "0" ;
х 2 else {s += "1" ; r -= 1.0d ;}
---------
1, 696 (-1) }
х 2 Console.WriteLine("Binary = {0}", s);
---------
1, 392 (-1 )
х 2
---------
0, 784 (0) ( 0, 678 )10= =( 0, 1010110 ...) 2
16
Лабораторна робота №4.
Обробка одновимірних масивів в C# .NET
17
Для оголошення одновимірного масиву в C# використовується
наступний синтаксис:
type[] arrayName; тип даних [] назва_масиву;
Приклади: int[] b; double[] Weight;
На відміну від С++, де масиви можуть створюватись як статично
так і динамічно, у C# всі масиви є динамічними, тому, у наведених
прикладах ідентифікатори “b” та “Weight” фактично є посиланнями на
майбутні масиви.
Спроба використати таке посилання до його ініціалізації
призводить до помилки компіляції.
Масиви у C# створюються (ініціалізуються) за допомогою
оператора new, синтаксис якого для одновимірних масивів
аналогічний синтаксису відповідного оператора мови С++:
18
Для обробки масивів, як правило, використовуються цикли
з лічильником-параметром for. При спробі звернутись до елемента
масиву за індексом, що виходить за допустимі межі, середовище
виконання CLR генерує виключення IndexOutOfRangeException
(“Індекс знаходиться поза межами масиву”).
Приклад заповнення масиву випадковими числами:
int[] myArray = new int[100];
Random RndGen = new Random ();
for (int i = 0; i < myArray.Length; i++)
{ myArray[i] = RndGen.Next (10);}
Якщо необхідно послідовно отримати доступ до значення
кожного елемента масиву, доцільно використовувати спеціальний
оператор foreach (англ. for each - для кожного).
Приклад:
double[] x;
x = new double[] {0.1, 0.2, 0.3};
foreach ( double Val in x)
{System.Write (Val + “ “); }
19
Приклад.
/// Створюємо масив типу string розмірності 5
Array myArr = Array.CreateInstance(typeof(string),5);
/// Ініціалізуємо перші два елементи масиву
myArr.SetValue("Name",0); myArr.SetValue("Age",1);
/// Зчитуємо дані з масиву
string s = (string)myArr.GetValue(1);
20
Приклад 4.2. Обчислити суму від’ємних елементів заданого дійсно-
чисельного масиву розмірності N.
/// обчислення суми від’ємних елементів масиву
double S = 0.0d;
for (int i=0;i<N;i++) if (X[i]<0)S+=X[i];
Console.WriteLine(
"Сума від’ємних елементів масиву S = {0}", S);
21
Приклад 4.7. В заданому масиві X розмірності N змінити порядок
розташування елементів на протилежний без використання
додаткового масиву.
Варіант 1 (без використання методу Array.Reverse)
double T ;
for (int i=0; i < N/2 ; i++)
{ T = X[i] ; X [i]= X[N-i-1]; X [N-i-1]= T; }
Варіант 2 (з використанням методу Array.Reverse)
Array.Reverse(X);
22
Приклад 4.10.
string[] arr2 = new string[5];
/// Створюємо масив типу string розмірності 5
Array myArr = Array.CreateInstance(typeof(string),5);
/// Ініціалізуємо перші три елементи масиву
myArr.SetValue("Name",0);
myArr.SetValue("Age",1);
myArr.SetValue("Adress",2);
/// Копіюємо масив методом Clone()
string[] arr1 = (string[])myArr.Clone();
foreach (string s in arr1) Console.Write(s+"");
Console.WriteLine();
/// Копіюємо масив методом Copy()
Array.Copy(myArr, arr2, myArr.Length);
foreach (string s in arr2) Console.Write(s+" ");
Console.WriteLine();
23
Лабораторна робота №5.
Алгоритми сортування, пошуку та злиття
одновимірних масивів в C# .NET
24
Метод бульбашки (англ. Bubble sort) полягає в порівнянні сусідніх
елементів масиву між собою з їх перестановкою у випадку, якщо їх
розташування не відповідає потрібному порядку сортування (тобто у
випадку інверсії). В результаті одного перегляду масиву самий
“важкий” елемент витісняється в кінець масиву, тому для наступного
перегляду довжину масиву можна зменшити на одиницю. Метод
названий за аналогією з поведінкою повітряних бульбашок у склянці
кока-коли, коли самі великі бульбашки відриваються від дна і
досягають поверхні першими.
/// Сортування методом бульбашки (Bubble sort)
private static void BubbleSort(ref int[] A)
{for ( int L=A.Length;L>1; L--)
for ( int K=0;K<L-1;K++)
if ( A[K] > A[K+1] ) Swap(ref A[K], ref A[K+1]);
}
На практиці метод бульбашки застосовується для сортування майже
впорядкованих масивів та файлів баз даних, які виникають при
періодичному доповненні відсортованих раніше новими елементами.
Сортування змішуванням (англ. Cocktail sort) — вдосконалений
метод бульбашки. Аналізуючи метод бульбашки, відмічаємо дві
обставини:
1) Якщо при переміщенні вздовж частини масиву перестановки не
відбуваються, то ця частина масиву вже відсортована і далі
непотрібно її переглядати.
2) При переміщенні від початку до кінця масиву максимальний
елемент досягає кінця масиву, а мінімальний зміщується тільки на
одну позицію. Якщо рухатись в протилежному напрямі, то навпаки –
мінімальний елемент досягає свою позицію на початку масиву, а
максимальний рухається повільно.
Тому у вдосконаленому методі бульбашки запам’ятовують позиції
інверсій, і встановлюють межі наступного перегляду в місцях, де
відбувались останні перестановки. Масив переглядається по черзі
справа наліво і зліва направо.
/// Шейкерне сортування
static void CocktailSort(ref int[] A)
{int start, finish;
int countIf = 0;//лічильник порівнянь
int countSwap = 0;//лічильник перестановок
25
for (int i = 0; i < A.Length/2; i++)
{ start = 0; finish = A.Length - 1;
do
{ countIf += 2;
/// перегляд зліва направо
if (A[start] > A[start + 1])
{ Swap(ref A[start], ref A[start+1]); countSwap++;}
start++; /// переміщаємо ліву границю перегляду
/// перегляд справа наліво
if (A[finish-1] > A[finish])
{ Swap(ref A[finish-1], ref A[finish]); countSwap++;}
finish--; ///переміщаємо праву границю перегляду
} /// умова продовження переглядів
while (start <= finish);
}
Console.WriteLine("Кількість порівнянь = {0}",countIf);
Console.WriteLine
("Кількість перестановок = {0}",countSwap);
}
26
повністю або в заданих межах або відсортувати два масива, що
містять відповідні пари "ключ-значення". Після сортування в масиві
можна здійснювати пошук з допомогою ефективного алгоритму
бінарного пошуку, реалізованого в методі BinarySearch().
Приклад 5.1.
using System;
namespace ArraySorting
{class Program
{public static void Main(string[] args)
{ int[] A = { 4, 5, -183, 12, 34, 0, 2 ,-13 };
Console.WriteLine("Заданий масив: ");
foreach (int x in A) Console.Write("{0} ",x);
Console.WriteLine();
Console.WriteLine("Відсортований масив:");
Array.Sort(A);
foreach (int x in A)Console.Write("{0} ",x);
Console.WriteLine();
/// Бінарний пошук заданого елемента
/// у відсортованому масиві
Console.WriteLine("Пошук числа 12 у масиві А:");
int search = Array.BinarySearch(A, 12);
Console.WriteLine(
"Число 12 знаходиться на {0} позиції",search+1);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Завдання 5.1.
• Створити цілочисельний масив А розмірності N=100.
int N=100; int[] A = new int[N];
• Заповнити масив випадковими числами в діапазоні [-N, N-1].
/// створення об’єкту класу Random
Random R = new Random();
/// цикл для всіх індексів масиву А
for (int i = 0; i < A.Length; i++)
/// одержуємо ціле невід’ємне число
/// в діапазоні[0,2*N-1]
/// віднявши N одержимо діапазон [-N, N-1]
A[i] = R.Next (2*N)-N;
• Вивести масив А на консоль по 10 елементів в рядку
27
/// виведення елементів масиву
Console.WriteLine("Масив A")
for (int i=0;i<N;i++)
{Console.Write("{0} \t",A[i]);
if (i%10==9) Console.WriteLine();}
Console.WriteLine();
• Дослідити створений масив на впорядкованість (обчислити
кількість інверсій).
int Inv = 0;
for (int i=0;i<N-1;i++)
if (A[i]>A[i+1]) Inv ++;
Console.WriteLine("Кількість інверсій = {0}", Inv);
• Створити копію А1 масиву А
/// Копіюємо масив методом Clone()
int[] A1 = (int[])A.Clone();
• Відсортувати масив А1 методом швидкого сортування Array.Sort.
Array.Sort(A1);
• Вивести відсортований масив А1 на консоль по 10 елементів в
рядку.
• Перевірити відсортований масив А1 на впорядкованість
(обчислити кількість інверсій).
Inv = 0;
for (int i=0;i<N-1;i++)
if (A1[i]>A1[i+1]) Inv ++;
Console.WriteLine("Кількість інверсій = {0}", Inv);
• Створити нову копію А1 масиву А.
/// Копіюємо масив методом Clone()
A1 = (int[])A.Clone();
• Відсортувати масив А1 вдосконаленим методом бульбашки (метод
перемішування або шейкерне сортування).
CocktailSort(ref A1);
• Вивести відсортований масив А1 на консоль по 10 елементів в
рядку.
• Перевірити відсортований масив А1 на впорядкованість
(обчислити кількість інверсій).
• Відлагодити програму.
• Додати системні функції для вимірювання проміжків часу між
початком і закінченням роботи алгоритмів сортування.
using System.Diagnostics; // додати на початку програми
28
Stopwatch T = new Stopwatch();
T.Start();
Array.Sort(A1); /// сортування масиву (QuickSort)
T.Stop();
Console.WriteLine
("час сортування методом QuickSort {0} ",T.Elapsed);
T.Reset();
T.Start();
CocktailSort(ref A1);
T.Stop();
Console.WriteLine
("час сортування методом CocktailSort {0} ",T.Elapsed);
• Відключити (коментарями /* */) код для виведення масивів.
• Збільшити розмірність масивів (N=10000).
Console.WriteLine("кількість елементів N={0}”,N);
• Порівняти час сортування обома алгоритмами.
• Застосувати алгоритм швидкого сортування до вже відсортованого
масиву (з вимірюванням часу).
• Застосувати алгоритм шейкерного сортування до вже
відсортованого масиву (з вимірюванням часу).
• Порівняти час роботи обох алгоритмів для вже відсортованого
масиву.
• Змінити розташування елементів у вже відсортованому масиві А1
на протилежне методом Array.Reverse.
Array.Reverse(A1);
• Обчислити кількість інверсій.
• Застосувати алгоритм швидкого сортування до відсортованого в
протилежному порядку масиву А1 (з вимірюванням часу).
• Змінити розташування елементів у вже відсортованому масиві А1
на протилежне методом Array.Reverse.
Array.Reverse(A1);
• Застосувати алгоритм шейкерного сортування до відсортованого в
протилежному порядку масиву А1 (з вимірюванням часу).
• Порівняти час роботи обох алгоритмів для відсортованого в
протилежному порядку масиву. Записати результати та висновки в
звіт.
• Додати до звіту відлагоджені тексти програм та скріншоти з
початковим та відсортованим масивами (для N=100).
29
5.2. Злиття відсортованих масивів
Завдання 5.2.
• Створити цілочисельний масив А розмірності N=100.
int N=100; int[] A = new int[N];
• Заповнити масив випадковими числами в діапазоні [0, N-1].
• Вивести масив A на консоль по 10 елементів в рядку.
• Відсортувати масив А методом швидкого сортування Array.Sort.
Array.Sort(A);
• Вивести відсортований масив A на консоль по 10 елементів в
рядку.
• Перевірити відсортований масив на впорядкованість (обчислити
кількість інверсій).
• Створити цілочисельний масив B розмірності M=200.
int M=200; int[] B = new int[M];
• Заповнити масив випадковими числами в діапазоні [0, M-1].
• Вивести масив B на консоль по 10 елементів в рядку.
• Відсортувати масив B методом швидкого сортування Array.Sort.
Array.Sort(B);
• Вивести відсортований масив B на консоль по 10 елементів в
рядку.
• Перевірити відсортований масив на впорядкованість (обчислити
кількість інверсій).
• Об’єднати (злити) відсортовані масиви в новий масив Z
розмірності N+M так, щоб елементи в новому масиві були
впорядковані.
З відсортованих масивів беремо по одному елементу, порівнюємо між
собою і менший записуємо в результуючий масив. Для контролю за
процесами вибірки та занесення для кожного з трьох масивів A, B, Z
використовується свій лічильник a , b , k. Якщо з одного масиву
вибрано всі елементи, в результуючий дописується залишок іншого.
Приклад 5.2.
/// Злиття двох відсортованих масивів
int [] Z = new int[N+M]; int a = 0 ; int b = 0 ;
for ( int k = 0 ; k<N+M ; k++)
{if ( a >= N) { Z[ k ] = B [ b ] ; b++ ; continue ;}
if ( b >= M) { Z[ k ] = A [ a ] ; a++ ; continue ;}
if (A[ a ] <= B [ b ]) { Z[ k ] = A [ a ]; a++ ; }
else { Z[ k ] = B [ b ] ; b++ ; }
}
30
• Вивести масив Z на консоль по 10 елементів в рядку.
• Перевірити масив Z на впорядкованість (обчислити кількість
інверсій).
• Додати до звіту відлагоджений текст програм та скріншоти з
початковими та результуючим масивами.
•
5.3. Алгоритми пошуку значень елементів в масивах
5.3.1. Лінійний пошук
Використовується для пошуку одного елемента (або декількох рівних
елементів) з потрібним значенням у заданому невідсортованому
масиві.
Лінійний пошук - алгоритм послідовного перегляду масиву і
порівняння всіх елементів з шуканим значенням. Є найпростішим
алгоритмом пошуку, не накладає жодних обмежень на масив і має
просту реалізацію. У зв'язку з малою ефективністю в порівнянні з
іншими алгоритмами лінійний пошук зазвичай використовують лише
тоді, коли відрізок пошукової системи містить дуже мало елементів,
однак лінійний пошук не вимагає додаткової пам'яті або
обробки/аналізу масиву.
Приклад 5.3.
static int LinearSearch(int[] A, key)
{ L=-1;
for ( int i=0; i < A.Length ; i++)
if ( A[i] == key){ L = I ; break }
return L;
}
Тут Arr.Length - це розмірність масиву, key - значення яке ми
шукаємо, якщо елемент масиву співпадає з key, повертаємо його
індекс. Якщо елемент із заданим значенням відсутний, --
повертається -1.
5.3.2. Двійковий (бінарний) пошук
Також називають методом поділу пополам або дихотомія.
Використовується для швидкого пошуку потрібної інформації у
заданому відсортованому масиві.
Дихотомічний пошук (двійковий пошук) — алгоритм знаходження
заданого значення у впорядкованомумасиві, який полягає у порівнянні
серединного елемента масиву з шуканим значенням, і повторенням
алгоритму для тієї або іншої половини, залежно від результату
31
порівняння. Зазвичай реалізується методом рекурсії, або ітерації.
Основна вимога до масиву-впорядкованість.
Приклад реалізації дихотомічного пошуку рекурсивним методом.
Приклад 5.4.
static void BinarySearch(int[] A, value, low, high)
{ if (high < low) return -1 ; // не знайдено
mid = (low + high) / 2 ;
if (A[mid] > value)
return BinarySearch( A, value, low, mid-1);
else
if (A[mid] < value)
return BinarySearch( A, value, mid+1, high)
else return mid ; // знайдено
}
32